All Files (45.39% covered at 6.02 hits/line)
969 files in total.
40749 relevant lines.
18494 lines covered and
22255 lines missed
-
1
class ApplicationController < ActionController::Base
-
1
protect_from_forgery
-
-
1
def index
-
end
-
end
-
1
class CompaniesController < ApplicationController
-
-
1
def create
-
@company = Company.new
-
if @company.update_with(params[:company])
-
flash[:notice] = "#{@company.name} was created."
-
redirect_to company_path(@company)
-
else
-
flash[:error] = "Cannot create new company. #{@company.errors.messages}"
-
redirect_to new_company_path
-
end
-
end
-
-
1
def new
-
@company = Company.new
-
@company.build_phone_number
-
end
-
-
1
def edit
-
@company = Company.find(params[:id])
-
end
-
-
1
def show
-
@company = Company.find(params[:id])
-
end
-
-
1
def update
-
@company = Company.find(params[:id])
-
if @company.update_with(params[:"#{@company.type.underscore}"])
-
flash[:notice] = "#{@company.name} was updated."
-
else
-
flash[:error] = "Cannot update #{@company.name}. #{@company.errors.messages}"
-
end
-
redirect_to company_path(@company)
-
end
-
-
1
def destroy
-
@company = Company.find(params[:id])
-
@company.destroy
-
flash[:notice] = "Company '#{@company.name}' was deleted."
-
redirect_to companies_path
-
end
-
-
end
-
1
class ContactsController < ApplicationController
-
1
def search
-
@search_term = params[:search]
-
@individuals = @search_term ? Individual.search(@search_term) : Individual.all
-
@individuals = @individuals.paginate(page: params[:individuals_page], per_page: 10)
-
@board_members = @search_term ? BoardMember.search(@search_term) : BoardMember.all
-
@board_members = @board_members.paginate(page: params[:board_members_page], per_page: 10)
-
@advisors = @search_term ? Advisor.search(@search_term) : Advisor.all
-
@advisors = @advisors.paginate(page: params[:advisors_page], per_page: 10)
-
@education_companies = @search_term ? EducationCompany.search(@search_term) : EducationCompany.all
-
@education_companies = @education_companies.paginate(page: params[:education_companies_page], per_page: 10)
-
@portfolio_companies = @search_term ? PortfolioCompany.search(@search_term) : PortfolioCompany.all
-
@portfolio_companies = @portfolio_companies.paginate(page: params[:portfolio_companies_page], per_page: 10)
-
@professional_service_providers = @search_term ? ProfessionalServiceProvider.search(@search_term) : ProfessionalServiceProvider.all
-
@professional_service_providers = @professional_service_providers.paginate(page: params[:professaionl_service_providers_page], per_page: 10)
-
end
-
end
-
1
class PeopleController < ApplicationController
-
-
1
def index
-
@search_term = params[:search]
-
@individuals = @search_term ? Individual.search(@search_term) : Individual.all
-
# @individuals = @individuals.paginate(page: params[:page])
-
@board_members = @search_term ? BoardMember.search(@search_term) : BoardMember.all
-
# @board_members = @board_members.paginate(page: params[:page])
-
@advisors = @search_term ? Advisor.search(@search_term) : Advisor.all
-
# @advisors = @advisors.paginate(page: params[:page])
-
end
-
-
1
def create
-
@person = Person.new
-
if @person.update_with(params[:person])
-
flash[:notice] = "#{@person.full_name} was created."
-
redirect_to person_path(@person)
-
else
-
flash[:error] = "Cannot create new person. #{@person.errors.full_messages}"
-
redirect_to new_person_path
-
end
-
end
-
-
1
def new
-
@person = Person.new
-
@person.build_phone_number
-
end
-
-
1
def edit
-
@person = Person.find(params[:id])
-
end
-
-
1
def show
-
@person = Person.find(params[:id])
-
end
-
-
1
def update
-
@person = Person.find(params[:id])
-
if @person.update_with(params[:"#{@person.type.underscore}"])
-
flash[:notice] = "#{@person.full_name} was updated."
-
else
-
flash[:error] = "Cannot update #{@person.full_name}. #{@person.errors.full_messages}"
-
end
-
redirect_to person_path(@person)
-
end
-
-
1
def destroy
-
@person = Person.find(params[:id])
-
@person.destroy
-
flash[:notice] = "Person '#{@person.full_name}' was deleted."
-
redirect_to search_path
-
end
-
-
end
-
1
module ApplicationHelper
-
end
-
1
module CompaniesHelper
-
end
-
1
module ContactsHelper
-
end
-
1
module PeopleHelper
-
end
-
1
class Advisor < Person
-
end
-
1
class BoardMember < Person
-
end
-
1
class Company < ActiveRecord::Base
-
1
has_one :phone_number, :as => :callable, :dependent => :destroy
-
1
accepts_nested_attributes_for :phone_number
-
1
belongs_to :representative, :foreign_key => "representative_id", :class_name => "Person"
-
-
1
validates :type, :inclusion => { :in => %w(EducationCompany PortfolioCompany ProfessionalServiceProvider) }
-
1
validates :name, :presence => true
-
1
validates :representative_role, :presence => true
-
-
# Mixes in the update_with method since it is shared between this an Company
-
1
include Contact
-
-
1
def self.search(search_term)
-
if search_term
-
where('name LIKE ?', "%#{search_term}%")
-
end
-
end
-
end
-
1
module Contact
-
1
def update_with(attributes)
-
# Type is separately updated because, as a single table inheritance specific
-
# property, it cannot be mass assigned with update_attributes.
-
if self.type != attributes[:type]
-
self.type = attributes[:type]
-
if !self.new_record?
-
self.save
-
end
-
# The object needs to be reloaded after changing type for the SQL statement
-
# to work properly and not call on the old class.
-
updated_contact = self.becomes(self.type.constantize)
-
else
-
updated_contact = self
-
end
-
updated_contact.update_attributes(attributes.except(:type))
-
if updated_contact.invalid?
-
updated_contact.errors.each do |attr, msg|
-
self.errors.add(attr, msg)
-
end
-
false
-
else
-
true
-
end
-
end
-
end
-
1
class EducationCompany < Company
-
end
-
1
class Individual < Person
-
end
-
1
class Person < ActiveRecord::Base
-
1
has_one :phone_number, :as => :callable, :dependent => :destroy
-
1
accepts_nested_attributes_for :phone_number
-
-
1
has_one :company, :foreign_key => "representative_id"
-
-
1
validates :type, :inclusion => { :in => %w(Individual BoardMember Advisor) }
-
1
validates :first_name, :presence => true
-
1
validates :last_name, :presence => true
-
1
validates :occupation, :presence => true
-
1
validates_associated :phone_number
-
-
# Mixes in the update_with method since it is shared between this an Company
-
1
include Contact
-
-
1
def full_name
-
"#{first_name} #{last_name}"
-
end
-
-
1
def self.search(search_term)
-
if search_term
-
where('first_name LIKE ? OR last_name LIKE ?', "%#{search_term}%", "%#{search_term}%")
-
end
-
end
-
-
1
def self.find_by_full_name(full_name)
-
where('first_name LIKE ? AND last_name LIKE ?', full_name.split(" ")[0], full_name.split(" ")[1])
-
end
-
end
-
1
class PhoneNumber < ActiveRecord::Base
-
1
belongs_to :callable, :polymorphic => true
-
1
validates :label, :inclusion => { :in => %w(Home Business Mobile Other) }
-
end
-
1
class PortfolioCompany < Company
-
end
-
1
class ProfessionalServiceProvider < Company
-
end
-
1
require File.expand_path('../boot', __FILE__)
-
-
1
require 'rails/all'
-
-
1
if defined?(Bundler)
-
# If you precompile assets before deploying to production, use this line
-
1
Bundler.require *Rails.groups(:assets => %w(development test))
-
# If you want your assets lazily compiled in production, use this line
-
# Bundler.require(:default, :assets, Rails.env)
-
end
-
-
1
module ICA
-
1
class Application < Rails::Application
-
# Settings in config/environments/* take precedence over those specified here.
-
# Application configuration should go into files in config/initializers
-
# -- all .rb files in that directory are automatically loaded.
-
-
# Custom directories with classes and modules you want to be autoloadable.
-
# config.autoload_paths += %W(#{config.root}/extras)
-
-
# Only load the plugins named here, in the order given (default is alphabetical).
-
# :all can be used as a placeholder for all plugins not explicitly named.
-
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
-
-
# Activate observers that should always be running.
-
# config.active_record.observers = :cacher, :garbage_collector, :forum_observer
-
-
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
-
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
-
# config.time_zone = 'Central Time (US & Canada)'
-
-
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
-
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
-
# config.i18n.default_locale = :de
-
-
# Configure the default encoding used in templates for Ruby 1.9.
-
1
config.encoding = "utf-8"
-
-
# Configure sensitive parameters which will be filtered from the log file.
-
1
config.filter_parameters += [:password]
-
-
# Enable the asset pipeline
-
1
config.assets.enabled = true
-
-
# Version of your assets, change this if you want to expire all your assets
-
1
config.assets.version = '1.0'
-
-
# Remove field_with_error div wrapping invalid classes since it messes with Bootstrap styling
-
1
config.action_view.field_error_proc = Proc.new { |html_tag, instance| "#{html_tag}".html_safe }
-
end
-
end
-
1
require 'rubygems'
-
-
# Set up gems listed in the Gemfile.
-
1
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
-
-
1
require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
-
# Load the rails application
-
1
require File.expand_path('../application', __FILE__)
-
-
# Initialize the rails application
-
1
ICA::Application.initialize!
-
1
ICA::Application.configure do
-
# Settings specified here will take precedence over those in config/application.rb
-
-
# The test environment is used exclusively to run your application's
-
# test suite. You never need to work with it otherwise. Remember that
-
# your test database is "scratch space" for the test suite and is wiped
-
# and recreated between test runs. Don't rely on the data there!
-
1
config.cache_classes = true
-
-
# Configure static asset server for tests with Cache-Control for performance
-
1
config.serve_static_assets = true
-
1
config.static_cache_control = "public, max-age=3600"
-
-
# Log error messages when you accidentally call methods on nil
-
1
config.whiny_nils = true
-
-
# Show full error reports and disable caching
-
1
config.consider_all_requests_local = true
-
1
config.action_controller.perform_caching = false
-
-
# Raise exceptions instead of rendering exception templates
-
1
config.action_dispatch.show_exceptions = false
-
-
# Disable request forgery protection in test environment
-
1
config.action_controller.allow_forgery_protection = false
-
-
# Tell Action Mailer not to deliver emails to the real world.
-
# The :test delivery method accumulates sent emails in the
-
# ActionMailer::Base.deliveries array.
-
1
config.action_mailer.delivery_method = :test
-
-
# Use SQL instead of Active Record's schema dumper when creating the test database.
-
# This is necessary if your schema can't be completely dumped by the schema dumper,
-
# like if you have constraints or database-specific column types
-
# config.active_record.schema_format = :sql
-
-
# Print deprecation notices to the stderr
-
1
config.active_support.deprecation = :stderr
-
-
# Allow pass debug_assets=true as a query parameter to load pages with unpackaged assets
-
1
config.assets.allow_debugging = true
-
end
-
# Be sure to restart your server when you modify this file.
-
-
# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
-
# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
-
-
# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
-
# Rails.backtrace_cleaner.remove_silencers!
-
# Be sure to restart your server when you modify this file.
-
-
# Add new inflection rules using the following format
-
# (all these examples are active by default):
-
# ActiveSupport::Inflector.inflections do |inflect|
-
# inflect.plural /^(ox)$/i, '\1en'
-
# inflect.singular /^(ox)en/i, '\1'
-
# inflect.irregular 'person', 'people'
-
# inflect.uncountable %w( fish sheep )
-
# end
-
# Be sure to restart your server when you modify this file.
-
-
# Add new mime types for use in respond_to blocks:
-
# Mime::Type.register "text/richtext", :rtf
-
# Mime::Type.register_alias "text/html", :iphone
-
# Be sure to restart your server when you modify this file.
-
-
# Your secret key for verifying the integrity of signed cookies.
-
# If you change this key, all old signed cookies will become invalid!
-
# Make sure the secret is at least 30 characters and all random,
-
# no regular words or you'll be exposed to dictionary attacks.
-
1
ICA::Application.config.secret_token = 'f48c249436e68b038f970c8755b537f96bb48b1fa373a83999adb14f0796534e9cb28b8eb608575d0a60a587d83c907b12ecf69ed84546cc583e06e79b9cdfc8'
-
# Be sure to restart your server when you modify this file.
-
-
1
ICA::Application.config.session_store :cookie_store, key: '_ICA_session'
-
-
# Use the database for sessions instead of the cookie-based default,
-
# which shouldn't be used to store highly confidential information
-
# (create the session table with "rails generate session_migration")
-
# ICA::Application.config.session_store :active_record_store
-
# Added for will_paginate functionality on arrays (i.e. pagination on movies returned by AR find)
-
1
require 'will_paginate/array'
-
# Be sure to restart your server when you modify this file.
-
#
-
# This file contains settings for ActionController::ParamsWrapper which
-
# is enabled by default.
-
-
# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
-
1
ActiveSupport.on_load(:action_controller) do
-
1
wrap_parameters format: [:json]
-
end
-
-
# Disable root element in JSON by default.
-
1
ActiveSupport.on_load(:active_record) do
-
1
self.include_root_in_json = false
-
end
-
1
ICA::Application.routes.draw do
-
-
# The priority is based upon order of creation:
-
# first created -> highest priority.
-
-
# Sample of regular route:
-
# match 'products/:id' => 'catalog#view'
-
# Keep in mind you can assign values other than :controller and :action
-
-
1
match 'contacts' => 'contacts#search', :via => :get, :as => :search
-
-
# Needed for STI inheritance to be properly mapped by the form because
-
# convention of form_for will look for the route of the specific model
-
# rather than Person (i.e. looks for advisor_path instead of person_path)
-
1
match 'individual/:id' => 'people#update', :via => :put, :as => :individual
-
1
match 'board_member/:id' => 'people#update', :via => :put, :as => :board_member
-
1
match 'advisor/:id' => 'people#update', :via => :put, :as => :advisor
-
1
match 'education_company/:id' => 'companies#update', :via => :put, :as => :education_company
-
1
match 'portfolio_company/:id' => 'companies#update', :via => :put, :as => :portfolio_company
-
1
match 'professional_service_provider/:id' => 'companies#update', :via => :put, :as => :professional_service_provider
-
# Sample of named route:
-
# match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
-
# This route can be invoked with purchase_url(:id => product.id)
-
-
# Sample resource route (maps HTTP verbs to controller actions automatically):
-
# resources :products
-
1
resources :people
-
1
resources :companies
-
-
# Sample resource route with options:
-
# resources :products do
-
# member do
-
# get 'short'
-
# post 'toggle'
-
# end
-
#
-
# collection do
-
# get 'sold'
-
# end
-
# end
-
-
# Sample resource route with sub-resources:
-
# resources :products do
-
# resources :comments, :sales
-
# resource :seller
-
# end
-
-
# Sample resource route with more complex sub-resources
-
# resources :products do
-
# resources :comments
-
# resources :sales do
-
# get 'recent', :on => :collection
-
# end
-
# end
-
-
# Sample resource route within a namespace:
-
# namespace :admin do
-
# # Directs /admin/products/* to Admin::ProductsController
-
# # (app/controllers/admin/products_controller.rb)
-
# resources :products
-
# end
-
-
# You can have the root of your site routed with "root"
-
# just remember to delete public/index.html.
-
1
root :to => 'application#index'
-
-
# See how all your routes lay out with "rake routes"
-
-
# This is a legacy wild controller route that's not recommended for RESTful applications.
-
# Note: This route will make all actions in every controller accessible via GET requests.
-
# match ':controller(/:action(/:id(.:format)))'
-
end
-
1
Given /the following stakeholders exist/ do |people_table|
-
people_table.hashes.each do |person|
-
@person = Person.create!(person)
-
end
-
end
-
-
1
Then /I should see the complete global contacts list/ do
-
rows = Person.all.size
-
page.has_css?("table#people_table/tbody tr", :count => rows)
-
-
end
-
-
1
Then /I should see all records that contain "(.*)"/ do |term|
-
@filtered_list = Person.where('first_name LIKE ?', '#{term}')
-
@filtered_list += Person.where('last_name LIKE ?', '#{term}')
-
@filtered_list += Person.where('occupation LIKE ?', '#{term}')
-
-
@filtered_list.each do |elt|
-
step %Q{I should see "#{elt.first_name}"}
-
step %Q{I should see "#{elt.last_name}"}
-
step %Q{I should see "#{elt.occupation}"}
-
end
-
end
-
-
1
Then /I should not see any records/ do
-
page.has_css?("table#people_table/tbody tr", :count => 0)
-
end
-
-
1
Then /^"([^"]*)" field should be selected for "(.*)"$/ do |field, value|
-
page.has_xpath?("//select[@id = '#{field}']/option[text() = '#{value}']")
-
end
-
-
-
# TL;DR: YOU SHOULD DELETE THIS FILE
-
#
-
# This file was generated by Cucumber-Rails and is only here to get you a head start
-
# These step definitions are thin wrappers around the Capybara/Webrat API that lets you
-
# visit pages, interact with widgets and make assertions about page content.
-
#
-
# If you use these step definitions as basis for your features you will quickly end up
-
# with features that are:
-
#
-
# * Hard to maintain
-
# * Verbose to read
-
#
-
# A much better approach is to write your own higher level step definitions, following
-
# the advice in the following blog posts:
-
#
-
# * http://benmabey.com/2008/05/19/imperative-vs-declarative-scenarios-in-user-stories.html
-
# * http://dannorth.net/2011/01/31/whose-domain-is-it-anyway/
-
# * http://elabs.se/blog/15-you-re-cuking-it-wrong
-
#
-
-
-
1
require 'uri'
-
1
require 'cgi'
-
1
require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "paths"))
-
1
require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "selectors"))
-
-
1
module WithinHelpers
-
1
def with_scope(locator)
-
locator ? within(*selector_for(locator)) { yield } : yield
-
end
-
end
-
1
World(WithinHelpers)
-
-
# Single-line step scoper
-
1
When /^(.*) within (.*[^:])$/ do |step, parent|
-
with_scope(parent) { When step }
-
end
-
-
# Multi-line step scoper
-
1
When /^(.*) within (.*[^:]):$/ do |step, parent, table_or_string|
-
with_scope(parent) { When "#{step}:", table_or_string }
-
end
-
-
1
Given /^(?:|I )am on (.+)$/ do |page_name|
-
visit path_to(page_name)
-
end
-
-
1
When /^(?:|I )go to (.+)$/ do |page_name|
-
visit path_to(page_name)
-
end
-
-
1
When /^(?:|I )press "([^"]*)"$/ do |button|
-
click_button(button)
-
end
-
-
1
When /^(?:|I )follow "([^"]*)"$/ do |link|
-
click_link(link)
-
end
-
-
1
When /^(?:|I )fill in "([^"]*)" with "([^"]*)"$/ do |field, value|
-
fill_in(field, :with => value)
-
end
-
-
1
When /^(?:|I )fill in "([^"]*)" for "([^"]*)"$/ do |value, field|
-
fill_in(field, :with => value)
-
end
-
-
# Use this to fill in an entire form with data from a table. Example:
-
#
-
# When I fill in the following:
-
# | Account Number | 5002 |
-
# | Expiry date | 2009-11-01 |
-
# | Note | Nice guy |
-
# | Wants Email? | |
-
#
-
# TODO: Add support for checkbox, select or option
-
# based on naming conventions.
-
#
-
1
When /^(?:|I )fill in the following:$/ do |fields|
-
fields.rows_hash.each do |name, value|
-
When %{I fill in "#{name}" with "#{value}"}
-
end
-
end
-
-
1
When /^(?:|I )select "([^"]*)" from "([^"]*)"$/ do |value, field|
-
select(value, :from => field)
-
end
-
-
1
When /^(?:|I )check "([^"]*)"$/ do |field|
-
check(field)
-
end
-
-
1
When /^(?:|I )uncheck "([^"]*)"$/ do |field|
-
uncheck(field)
-
end
-
-
1
When /^(?:|I )choose "([^"]*)"$/ do |field|
-
choose(field)
-
end
-
-
1
When /^(?:|I )attach the file "([^"]*)" to "([^"]*)"$/ do |path, field|
-
attach_file(field, File.expand_path(path))
-
end
-
-
1
Then /^(?:|I )should see "([^"]*)"$/ do |text|
-
if page.respond_to? :should
-
page.should have_content(text)
-
else
-
assert page.has_content?(text)
-
end
-
end
-
-
1
Then /^(?:|I )should see \/([^\/]*)\/$/ do |regexp|
-
regexp = Regexp.new(regexp)
-
-
if page.respond_to? :should
-
page.should have_xpath('//*', :text => regexp)
-
else
-
assert page.has_xpath?('//*', :text => regexp)
-
end
-
end
-
-
1
Then /^(?:|I )should not see "([^"]*)"$/ do |text|
-
if page.respond_to? :should
-
page.should have_no_content(text)
-
else
-
assert page.has_no_content?(text)
-
end
-
end
-
-
1
Then /^(?:|I )should not see \/([^\/]*)\/$/ do |regexp|
-
regexp = Regexp.new(regexp)
-
-
if page.respond_to? :should
-
page.should have_no_xpath('//*', :text => regexp)
-
else
-
assert page.has_no_xpath?('//*', :text => regexp)
-
end
-
end
-
-
1
Then /^the "([^"]*)" field(?: within (.*))? should contain "([^"]*)"$/ do |field, parent, value|
-
with_scope(parent) do
-
field = find_field(field)
-
field_value = (field.tag_name == 'textarea') ? field.text : field.value
-
if field_value.respond_to? :should
-
field_value.should =~ /#{value}/
-
else
-
assert_match(/#{value}/, field_value)
-
end
-
end
-
end
-
-
1
Then /^the "([^"]*)" field(?: within (.*))? should not contain "([^"]*)"$/ do |field, parent, value|
-
with_scope(parent) do
-
field = find_field(field)
-
field_value = (field.tag_name == 'textarea') ? field.text : field.value
-
if field_value.respond_to? :should_not
-
field_value.should_not =~ /#{value}/
-
else
-
assert_no_match(/#{value}/, field_value)
-
end
-
end
-
end
-
-
1
Then /^the "([^"]*)" field should have the error "([^"]*)"$/ do |field, error_message|
-
element = find_field(field)
-
classes = element.find(:xpath, '..')[:class].split(' ')
-
-
form_for_input = element.find(:xpath, 'ancestor::form[1]')
-
using_formtastic = form_for_input[:class].include?('formtastic')
-
error_class = using_formtastic ? 'error' : 'field_with_errors'
-
-
if classes.respond_to? :should
-
classes.should include(error_class)
-
else
-
assert classes.include?(error_class)
-
end
-
-
if page.respond_to?(:should)
-
if using_formtastic
-
error_paragraph = element.find(:xpath, '../*[@class="inline-errors"][1]')
-
error_paragraph.should have_content(error_message)
-
else
-
page.should have_content("#{field.titlecase} #{error_message}")
-
end
-
else
-
if using_formtastic
-
error_paragraph = element.find(:xpath, '../*[@class="inline-errors"][1]')
-
assert error_paragraph.has_content?(error_message)
-
else
-
assert page.has_content?("#{field.titlecase} #{error_message}")
-
end
-
end
-
end
-
-
1
Then /^the "([^"]*)" field should have no error$/ do |field|
-
element = find_field(field)
-
classes = element.find(:xpath, '..')[:class].split(' ')
-
if classes.respond_to? :should
-
classes.should_not include('field_with_errors')
-
classes.should_not include('error')
-
else
-
assert !classes.include?('field_with_errors')
-
assert !classes.include?('error')
-
end
-
end
-
-
1
Then /^the "([^"]*)" checkbox(?: within (.*))? should be checked$/ do |label, parent|
-
with_scope(parent) do
-
field_checked = find_field(label)['checked']
-
if field_checked.respond_to? :should
-
field_checked.should be_true
-
else
-
assert field_checked
-
end
-
end
-
end
-
-
1
Then /^the "([^"]*)" checkbox(?: within (.*))? should not be checked$/ do |label, parent|
-
with_scope(parent) do
-
field_checked = find_field(label)['checked']
-
if field_checked.respond_to? :should
-
field_checked.should be_false
-
else
-
assert !field_checked
-
end
-
end
-
end
-
-
1
Then /^(?:|I )should be on (.+)$/ do |page_name|
-
current_path = URI.parse(current_url).path
-
if current_path.respond_to? :should
-
current_path.should == path_to(page_name)
-
else
-
assert_equal path_to(page_name), current_path
-
end
-
end
-
-
1
Then /^(?:|I )should have the following query string:$/ do |expected_pairs|
-
query = URI.parse(current_url).query
-
actual_params = query ? CGI.parse(query) : {}
-
expected_params = {}
-
expected_pairs.rows_hash.each_pair{|k,v| expected_params[k] = v.split(',')}
-
-
if actual_params.respond_to? :should
-
actual_params.should == expected_params
-
else
-
assert_equal expected_params, actual_params
-
end
-
end
-
-
1
Then /^show me the page$/ do
-
save_and_open_page
-
end
-
# TL;DR: YOU SHOULD DELETE THIS FILE
-
#
-
# This file is used by web_steps.rb, which you should also delete
-
#
-
# You have been warned
-
1
module NavigationHelpers
-
# Maps a name to a path. Used by the
-
#
-
# When /^I go to (.+)$/ do |page_name|
-
#
-
# step definition in web_steps.rb
-
#
-
1
def path_to(page_name)
-
case page_name
-
-
when /^the home\s?page$/
-
'/'
-
when /^the "(.*)" page$/i
-
person_path(Person.find_by_full_name($1).first)
-
when /^the edit page for "(.*)"$/i
-
edit_person_path(Person.find_by_full_name($1).first)
-
when /^the create page$/
-
new_person_path
-
-
# Add more mappings here.
-
# Here is an example that pulls values out of the Regexp:
-
#
-
# when /^(.*)'s profile page$/i
-
# user_profile_path(User.find_by_login($1))
-
-
else
-
begin
-
page_name =~ /^the (.*) page$/
-
path_components = $1.split(/\s+/)
-
self.send(path_components.push('path').join('_').to_sym)
-
rescue NoMethodError, ArgumentError
-
raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
-
"Now, go and add a mapping in #{__FILE__}"
-
end
-
end
-
end
-
end
-
-
1
World(NavigationHelpers)
-
# TL;DR: YOU SHOULD DELETE THIS FILE
-
#
-
# This file is used by web_steps.rb, which you should also delete
-
#
-
# You have been warned
-
1
module HtmlSelectorsHelpers
-
# Maps a name to a selector. Used primarily by the
-
#
-
# When /^(.+) within (.+)$/ do |step, scope|
-
#
-
# step definitions in web_steps.rb
-
#
-
1
def selector_for(locator)
-
case locator
-
-
when "the page"
-
"html > body"
-
-
# Add more mappings here.
-
# Here is an example that pulls values out of the Regexp:
-
#
-
# when /^the (notice|error|info) flash$/
-
# ".flash.#{$1}"
-
-
# You can also return an array to use a different selector
-
# type, like:
-
#
-
# when /the header/
-
# [:xpath, "//header"]
-
-
# This allows you to provide a quoted selector as the scope
-
# for "within" steps as was previously the default for the
-
# web steps:
-
when /^"(.+)"$/
-
$1
-
-
else
-
raise "Can't find mapping from \"#{locator}\" to a selector.\n" +
-
"Now, go and add a mapping in #{__FILE__}"
-
end
-
end
-
end
-
-
1
World(HtmlSelectorsHelpers)
-
1
require 'find'
-
1
require 'rbconfig'
-
-
1
$TESTING = false unless defined? $TESTING
-
-
##
-
# Autotest continuously scans the files in your project for changes
-
# and runs the appropriate tests. Test failures are run until they
-
# have all passed. Then the full test suite is run to ensure that
-
# nothing else was inadvertantly broken.
-
#
-
# If you want Autotest to start over from the top, hit ^C once. If
-
# you want Autotest to quit, hit ^C twice.
-
#
-
# Rails:
-
#
-
# The autotest command will automatically discover a Rails directory
-
# by looking for config/environment.rb. When Rails is discovered,
-
# autotest uses RailsAutotest to perform file mappings and other work.
-
# See RailsAutotest for details.
-
#
-
# Plugins:
-
#
-
# Plugins are available by creating a .autotest file either in your
-
# project root or in your home directory. You can then write event
-
# handlers in the form of:
-
#
-
# Autotest.add_hook hook_name { |autotest| ... }
-
#
-
# The available hooks are listed in +ALL_HOOKS+.
-
#
-
# See example_dot_autotest.rb for more details.
-
#
-
# If a hook returns a true value, it signals to autotest that the hook
-
# was handled and should not continue executing hooks.
-
#
-
# Naming:
-
#
-
# Autotest uses a simple naming scheme to figure out how to map
-
# implementation files to test files following the Test::Unit naming
-
# scheme.
-
#
-
# * Test files must be stored in test/
-
# * Test files names must start with test_
-
# * Test class names must start with Test
-
# * Implementation files must be stored in lib/
-
# * Implementation files must match up with a test file named
-
# test_.*implementation.rb
-
#
-
# Strategy:
-
#
-
# 1. Find all files and associate them from impl <-> test.
-
# 2. Run all tests.
-
# 3. Scan for failures.
-
# 4. Detect changes in ANY (ruby?. file, rerun all failures + changed files.
-
# 5. Until 0 defects, goto 3.
-
# 6. When 0 defects, goto 2.
-
-
1
class Autotest
-
-
1
RUBY19 = defined? Encoding
-
-
1
T0 = Time.at 0
-
-
1
ALL_HOOKS = [ :all_good, :died, :green, :initialize,
-
:post_initialize, :interrupt, :quit, :ran_command,
-
:red, :reset, :run_command, :updated, :waiting ]
-
-
1
def self.options
-
@@options ||= {}
-
end
-
-
1
def options
-
self.class.options
-
end
-
-
2
HOOKS = Hash.new { |h,k| h[k] = [] }
-
1
unless defined? WINDOZE then
-
1
WINDOZE = /mswin|mingw/ =~ RbConfig::CONFIG['host_os']
-
1
SEP = WINDOZE ? '&' : ';'
-
end
-
-
1
@@discoveries = []
-
-
1
def self.parse_options args = ARGV
-
require 'optparse'
-
options = {
-
:args => args.dup
-
}
-
-
OptionParser.new do |opts|
-
opts.banner = <<-BANNER.gsub(/^ /, '')
-
Continuous testing for your ruby app.
-
-
Autotest automatically tests code that has changed. It
-
assumes the code is in lib, and tests are in tests. Autotest
-
uses plugins to control what happens. You configure plugins
-
with require statements in the .autotest file in your
-
project base directory, and a default configuration for all
-
your projects in the .autotest file in your home directory.
-
-
Usage:
-
autotest [options]
-
BANNER
-
-
opts.on "-f", "--fast-start", "Do not run full tests at start" do
-
options[:no_full_after_start] = true
-
end
-
-
opts.on("-c", "--no-full-after-failed",
-
"Do not run all tests on red->green") do
-
options[:no_full_after_failed] = true
-
end
-
-
opts.on "-v", "--verbose", "Be annoyingly verbose (debugs .autotest)." do
-
options[:verbose] = true
-
end
-
-
opts.on "-q", "--quiet", "Be quiet." do
-
options[:quiet] = true
-
end
-
-
opts.on("-r", "--rc CONF", String, "Override path to config file") do |o|
-
options[:rc] = Array(o)
-
end
-
-
opts.on("-s", "--style STYLE", String,
-
"Manually specify test style. (default: autodiscover)") do |style|
-
options[:style] = Array(style)
-
end
-
-
opts.on("-w", "--warnings", "Turn on ruby warnings") do
-
$-w = true
-
end
-
-
opts.on "-h", "--help", "Show this." do
-
puts opts
-
exit 1
-
end
-
end.parse! args
-
-
Autotest.options.merge! options
-
-
options
-
end
-
-
##
-
# Calculates the autotest runner to use to run the tests.
-
#
-
# Can be overridden with --style, otherwise uses ::autodiscover.
-
-
1
def self.runner
-
style = options[:style] || Autotest.autodiscover
-
target = Autotest
-
-
unless style.empty? then
-
mod = "autotest/#{style.join "_"}"
-
puts "loading #{mod}"
-
begin
-
require mod
-
rescue LoadError
-
abort "Autotest style #{mod} doesn't seem to exist. Aborting."
-
end
-
target = Autotest.const_get(style.map {|s| s.capitalize}.join)
-
end
-
-
target
-
end
-
-
##
-
# Add a proc to the collection of discovery procs. See
-
# +autodiscover+.
-
-
1
def self.add_discovery &proc
-
@@discoveries << proc
-
end
-
-
##
-
# Automatically find all potential autotest runner styles by
-
# searching your loadpath, vendor/plugins, and rubygems for
-
# "autotest/discover.rb". If found, that file is loaded and it
-
# should register discovery procs with autotest using
-
# +add_discovery+. That proc should return one or more strings
-
# describing the user's current environment. Those styles are then
-
# combined to dynamically invoke an autotest plugin to suite your
-
# environment. That plugin should define a subclass of Autotest with
-
# a corresponding name.
-
#
-
# === Process:
-
#
-
# 1. All autotest/discover.rb files loaded.
-
# 2. Those procs determine your styles (eg ["rails", "rspec"]).
-
# 3. Require file by sorting styles and joining (eg 'autotest/rails_rspec').
-
# 4. Invoke run method on appropriate class (eg Autotest::RailsRspec.run).
-
#
-
# === Example autotest/discover.rb:
-
#
-
# Autotest.add_discovery do
-
# "rails" if File.exist? 'config/environment.rb'
-
# end
-
#
-
-
1
def self.autodiscover
-
require 'rubygems'
-
-
# *sigh*
-
#
-
# This is needed for rspec's hacky discovery mechanism. For some
-
# reason rspec2 added generators that create
-
# "autotest/discover.rb" right in the project directory instead of
-
# keeping it in the rspec gem and properly deciding that the
-
# project is an rspec based project or not. See the url for more
-
# details:
-
#
-
# http://rubyforge.org/tracker/?func=detail&atid=1678&aid=28775&group_id=419
-
#
-
# For the record, the sane way to do it is the bacon way:
-
#
-
# "Since version 1.0, there is autotest support. You need to tag
-
# your test directories (test/ or spec/) by creating an .bacon
-
# file there. Autotest then will find it."
-
#
-
# I'm submitting a counter-patch to rspec to fix stuff properly,
-
# but for now I'm stuck with this because their brokenness is
-
# documented in multiple books.
-
#
-
# I'm removing this code once a sane rspec goes out.
-
-
hacky_discovery = Gem::Specification.any? { |s| s.name =~ /^rspec/ }
-
$: << '.' if hacky_discovery
-
-
Gem.find_files("autotest/discover").each do |f|
-
load f
-
end
-
-
# call all discovery procs and determine the style to use
-
@@discoveries.map{ |proc| proc.call }.flatten.compact.sort.uniq
-
end
-
-
##
-
# Initialize and run the system.
-
-
1
def self.run
-
new.run
-
end
-
-
1
attr_writer :known_files
-
1
attr_accessor(:completed_re,
-
:extra_class_map,
-
:extra_files,
-
:failed_results_re,
-
:files_to_test,
-
:find_order,
-
:interrupted,
-
:latest_results,
-
:last_mtime,
-
:libs,
-
:order,
-
:output,
-
:prefix,
-
:results,
-
:sleep,
-
:tainted,
-
:testlib,
-
:find_directories,
-
:unit_diff,
-
:wants_to_quit)
-
-
1
alias tainted? tainted
-
-
##
-
# Initialize the instance and then load the user's .autotest file, if any.
-
-
1
def initialize
-
# these two are set directly because they're wrapped with
-
# add/remove/clear accessor methods
-
@exception_list = []
-
@test_mappings = []
-
@child = nil
-
-
self.completed_re =
-
/\d+ tests, \d+ assertions, \d+ failures, \d+ errors(, \d+ skips)?/
-
self.extra_class_map = {}
-
self.extra_files = []
-
self.failed_results_re = /^\s+\d+\) (?:Failure|Error):\n(.*?)\((.*?)\)/
-
self.files_to_test = new_hash_of_arrays
-
self.find_order = []
-
self.known_files = nil
-
self.libs = %w[. lib test].join(File::PATH_SEPARATOR)
-
self.order = :random
-
self.output = $stderr
-
self.prefix = nil
-
self.sleep = 1
-
self.testlib = "test/unit"
-
specified_directories = ARGV.reject { |arg| arg.start_with?("-") } # options are not directories
-
self.find_directories = specified_directories.empty? ? ['.'] : specified_directories
-
self.unit_diff = nil
-
self.latest_results = nil
-
-
# file in /lib -> run test in /test
-
self.add_mapping(/^lib\/.*\.rb$/) do |filename, _|
-
possible = File.basename(filename).gsub '_', '_?' # ' stupid emacs
-
files_matching %r%^test/.*#{possible}$%
-
end
-
-
# file in /test -> run it
-
self.add_mapping(/^test.*\/test_.*rb$/) do |filename, _|
-
filename
-
end
-
-
default_configs = [File.expand_path('~/.autotest'), './.autotest']
-
configs = options[:rc] || default_configs
-
-
configs.each do |f|
-
load f if File.exist? f
-
end
-
end
-
-
##
-
# Repeatedly run failed tests, then all tests, then wait for changes
-
# and carry on until killed.
-
-
1
def run
-
hook :initialize
-
hook :post_initialize
-
-
reset
-
add_sigint_handler
-
-
self.last_mtime = Time.now if options[:no_full_after_start]
-
-
loop do
-
begin # ^c handler
-
get_to_green
-
if tainted? and not options[:no_full_after_failed] then
-
rerun_all_tests
-
else
-
hook :all_good
-
end
-
wait_for_changes
-
rescue Interrupt
-
break if wants_to_quit
-
reset
-
end
-
end
-
hook :quit
-
rescue Exception => err
-
hook(:died, err) or raise err
-
end
-
-
##
-
# Keep running the tests after a change, until all pass.
-
-
1
def get_to_green
-
begin
-
run_tests
-
wait_for_changes unless all_good
-
end until all_good
-
end
-
-
##
-
# Look for files to test then run the tests and handle the results.
-
-
1
def run_tests
-
new_mtime = self.find_files_to_test
-
return unless new_mtime
-
self.last_mtime = new_mtime
-
-
cmd = self.make_test_cmd self.files_to_test
-
return if cmd.empty?
-
-
hook :run_command, cmd
-
-
puts cmd unless options[:quiet]
-
-
old_sync = $stdout.sync
-
$stdout.sync = true
-
self.results = []
-
line = []
-
begin
-
open "| #{cmd}", "r" do |f|
-
until f.eof? do
-
c = f.getc or break
-
if RUBY19 then
-
print c
-
else
-
putc c
-
end
-
line << c
-
if c == ?\n then
-
self.results << if RUBY19 then
-
line.join
-
else
-
line.pack "c*"
-
end
-
line.clear
-
end
-
end
-
end
-
ensure
-
$stdout.sync = old_sync
-
end
-
hook :ran_command
-
self.results = self.results.join
-
-
handle_results self.results
-
end
-
-
############################################################
-
# Utility Methods, not essential to reading of logic
-
-
##
-
# Installs a sigint handler.
-
-
1
def add_sigint_handler
-
trap 'INT' do
-
Process.kill "KILL", @child if @child
-
-
if self.interrupted then
-
self.wants_to_quit = true
-
else
-
unless hook :interrupt then
-
puts "Interrupt a second time to quit"
-
self.interrupted = true
-
Kernel.sleep 1.5
-
end
-
raise Interrupt, nil # let the run loop catch it
-
end
-
end
-
end
-
-
##
-
# Installs a sigquit handler
-
-
1
def add_sigquit_handler
-
trap 'QUIT' do
-
restart
-
end
-
end
-
-
1
def restart
-
Process.kill "KILL", @child if @child
-
-
cmd = [$0, *options[:args]]
-
-
index = $LOAD_PATH.index RbConfig::CONFIG["sitelibdir"]
-
-
if index then
-
extra = $LOAD_PATH[0...index]
-
cmd = [Gem.ruby, "-I", extra.join(":")] + cmd
-
end
-
-
puts cmd.join(" ") if options[:verbose]
-
-
exec(*cmd)
-
end
-
-
##
-
# If there are no files left to test (because they've all passed),
-
# then all is good.
-
-
1
def all_good
-
files_to_test.empty?
-
end
-
-
##
-
# Convert a path in a string, s, into a class name, changing
-
# underscores to CamelCase, etc.
-
-
1
def path_to_classname s
-
sep = File::SEPARATOR
-
f = s.sub(/^test#{sep}/, '').sub(/\.rb$/, '').split sep
-
f = f.map { |path| path.split(/_|(\d+)/).map { |seg| seg.capitalize }.join }
-
f = f.map { |path| path =~ /^Test/ ? path : "Test#{path}" }
-
-
f.join '::'
-
end
-
-
##
-
# Returns a hash mapping a file name to the known failures for that
-
# file.
-
-
1
def consolidate_failures failed
-
filters = new_hash_of_arrays
-
-
class_map = Hash[*self.find_order.grep(/^test/).map { |f| # TODO: ugly
-
[path_to_classname(f), f]
-
}.flatten]
-
class_map.merge! self.extra_class_map
-
-
failed.each do |method, klass|
-
if class_map.has_key? klass then
-
filters[class_map[klass]] << method
-
else
-
output.puts "Unable to map class #{klass} to a file"
-
end
-
end
-
-
filters
-
end
-
-
##
-
# Find the files to process, ignoring temporary files, source
-
# configuration management files, etc., and return a Hash mapping
-
# filename to modification time.
-
-
1
def find_files
-
result = {}
-
targets = self.find_directories + self.extra_files
-
self.find_order.clear
-
-
targets.each do |target|
-
order = []
-
Find.find target do |f|
-
Find.prune if f =~ self.exceptions
-
-
next if test ?d, f
-
next if f =~ /(swp|~|rej|orig)$/ # temporary/patch files
-
next if f =~ /^\.\/tmp/ # temporary dir, used by isolate
-
next if f =~ /\/\.?#/ # Emacs autosave/cvs merge files
-
-
filename = f.sub(/^\.\//, '')
-
-
result[filename] = File.stat(filename).mtime rescue next
-
order << filename
-
end
-
self.find_order.push(*order.sort)
-
end
-
-
result
-
end
-
-
##
-
# Find the files which have been modified, update the recorded
-
# timestamps, and use this to update the files to test. Returns
-
# the latest mtime of the files modified or nil when nothing was
-
# modified.
-
-
1
def find_files_to_test files = find_files
-
updated = files.select { |filename, mtime| self.last_mtime < mtime }
-
-
# nothing to update or initially run
-
unless updated.empty? || self.last_mtime.to_i == 0 then
-
p updated if options[:verbose]
-
-
hook :updated, updated
-
end
-
-
updated.map { |f,m| test_files_for f }.flatten.uniq.each do |filename|
-
self.files_to_test[filename] # creates key with default value
-
end
-
-
if updated.empty? then
-
nil
-
else
-
files.values.max
-
end
-
end
-
-
##
-
# Check results for failures, set the "bar" to red or green, and if
-
# there are failures record this.
-
-
1
def handle_results results
-
results = results.gsub(/\e\[\d+m/, '') # strip ascii color
-
failed = results.scan self.failed_results_re
-
completed = results[self.completed_re]
-
-
if completed then
-
completed = completed.scan(/(\d+) (\w+)/).map { |v, k| [k, v.to_i] }
-
-
self.latest_results = Hash[*completed.flatten]
-
self.files_to_test = consolidate_failures failed
-
-
color = self.files_to_test.empty? ? :green : :red
-
hook color unless $TESTING
-
else
-
self.latest_results = nil
-
end
-
-
self.tainted = true unless self.files_to_test.empty?
-
end
-
-
##
-
# Lazy accessor for the known_files hash.
-
-
1
def known_files
-
unless @known_files then
-
@known_files = Hash[*find_order.map { |f| [f, true] }.flatten]
-
end
-
@known_files
-
end
-
-
##
-
# Returns the base of the ruby command.
-
-
1
def ruby_cmd
-
"#{prefix}#{ruby} -I#{libs} -rubygems"
-
end
-
-
##
-
# Generate the commands to test the supplied files
-
-
1
def make_test_cmd files_to_test
-
cmds = []
-
full, partial = reorder(files_to_test).partition { |k,v| v.empty? }
-
diff = self.unit_diff
-
diff = " | #{diff}" if diff and diff !~ /^\|/
-
-
unless full.empty? then
-
classes = full.map {|k,v| k}.flatten.uniq
-
classes.unshift testlib
-
classes = classes.join " "
-
cmds << "#{ruby_cmd} -e \"%w[#{classes}].each { |f| require f }\"#{diff}"
-
end
-
-
partial.each do |klass, methods|
-
regexp = Regexp.union(*methods).source
-
cmds << "#{ruby_cmd} #{klass} -n \"/^(#{regexp})$/\"#{diff}"
-
end
-
-
cmds.join "#{SEP} "
-
end
-
-
1
def new_hash_of_arrays
-
Hash.new { |h,k| h[k] = [] }
-
end
-
-
1
def reorder files_to_test
-
case self.order
-
when :alpha then
-
files_to_test.sort_by { |k,v| k }
-
when :reverse then
-
files_to_test.sort_by { |k,v| k }.reverse
-
when :random then
-
max = files_to_test.size
-
files_to_test.sort_by { |k,v| rand max }
-
when :natural then
-
(self.find_order & files_to_test.keys).map { |f| [f, files_to_test[f]] }
-
else
-
raise "unknown order type: #{self.order.inspect}"
-
end
-
end
-
-
##
-
# Rerun the tests from cold (reset state)
-
-
1
def rerun_all_tests
-
reset
-
run_tests
-
-
hook :all_good if all_good
-
end
-
-
##
-
# Clear all state information about test failures and whether
-
# interrupts will kill autotest.
-
-
1
def reset
-
self.files_to_test.clear
-
self.find_order.clear
-
-
self.interrupted = false
-
self.known_files = nil
-
self.last_mtime = T0
-
self.tainted = false
-
self.wants_to_quit = false
-
-
hook :reset
-
end
-
-
##
-
# Determine and return the path of the ruby executable.
-
-
1
def ruby
-
ruby = ENV['RUBY']
-
ruby ||= File.join(RbConfig::CONFIG['bindir'],
-
RbConfig::CONFIG['ruby_install_name'])
-
-
ruby.gsub! File::SEPARATOR, File::ALT_SEPARATOR if File::ALT_SEPARATOR
-
-
return ruby
-
end
-
-
##
-
# Return the name of the file with the tests for filename by finding
-
# a +test_mapping+ that matches the file and executing the mapping's
-
# proc.
-
-
1
def test_files_for filename
-
result = @test_mappings.find { |file_re, ignored| filename =~ file_re }
-
-
p :test_file_for => [filename, result.first] if result and $DEBUG
-
-
result = result.nil? ? [] : [result.last.call(filename, $~)].flatten
-
-
output.puts "No tests matched #{filename}" if
-
(options[:verbose] or $TESTING) and result.empty?
-
-
result.sort.uniq.select { |f| known_files[f] }
-
end
-
-
##
-
# Sleep then look for files to test, until there are some.
-
-
1
def wait_for_changes
-
hook :waiting
-
Kernel.sleep self.sleep until find_files_to_test
-
end
-
-
############################################################
-
# File Mappings:
-
-
##
-
# Returns all known files in the codebase matching +regexp+.
-
-
1
def files_matching regexp
-
self.find_order.select { |k| k =~ regexp }
-
end
-
-
##
-
# Adds a file mapping, optionally prepending the mapping to the
-
# front of the list if +prepend+ is true. +regexp+ should match a
-
# file path in the codebase. +proc+ is passed a matched filename and
-
# Regexp.last_match. +proc+ should return an array of tests to run.
-
#
-
# For example, if test_helper.rb is modified, rerun all tests:
-
#
-
# at.add_mapping(/test_helper.rb/) do |f, _|
-
# at.files_matching(/^test.*rb$/)
-
# end
-
-
1
def add_mapping regexp, prepend = false, &proc
-
if prepend then
-
@test_mappings.unshift [regexp, proc]
-
else
-
@test_mappings.push [regexp, proc]
-
end
-
nil
-
end
-
-
##
-
# Removed a file mapping matching +regexp+.
-
-
1
def remove_mapping regexp
-
@test_mappings.delete_if do |k,v|
-
k == regexp
-
end
-
nil
-
end
-
-
##
-
# Clears all file mappings. This is DANGEROUS as it entirely
-
# disables autotest. You must add at least one file mapping that
-
# does a good job of rerunning appropriate tests.
-
-
1
def clear_mappings
-
@test_mappings.clear
-
nil
-
end
-
-
############################################################
-
# Exceptions:
-
-
##
-
# Adds +regexp+ to the list of exceptions for find_file. This must
-
# be called _before_ the exceptions are compiled.
-
-
1
def add_exception regexp
-
raise "exceptions already compiled" if defined? @exceptions
-
-
@exception_list << regexp
-
nil
-
end
-
-
##
-
# Removes +regexp+ to the list of exceptions for find_file. This
-
# must be called _before_ the exceptions are compiled.
-
-
1
def remove_exception regexp
-
raise "exceptions already compiled" if defined? @exceptions
-
@exception_list.delete regexp
-
nil
-
end
-
-
##
-
# Clears the list of exceptions for find_file. This must be called
-
# _before_ the exceptions are compiled.
-
-
1
def clear_exceptions
-
raise "exceptions already compiled" if defined? @exceptions
-
@exception_list.clear
-
nil
-
end
-
-
##
-
# Return a compiled regexp of exceptions for find_files or nil if no
-
# filtering should take place. This regexp is generated from
-
# +exception_list+.
-
-
1
def exceptions
-
unless defined? @exceptions then
-
@exceptions = if @exception_list.empty? then
-
nil
-
else
-
Regexp.union(*@exception_list)
-
end
-
end
-
-
@exceptions
-
end
-
-
############################################################
-
# Hooks:
-
-
##
-
# Call the event hook named +name+, passing in optional args
-
# depending on the hook itself.
-
#
-
# Returns false if no hook handled the event.
-
#
-
# === Hook Writers!
-
#
-
# This executes all registered hooks <em>until one returns truthy</em>.
-
# Pay attention to the return value of your block!
-
-
1
def hook name, *args
-
deprecated = {
-
# none currently
-
}
-
-
if deprecated[name] and not HOOKS[name].empty? then
-
warn "hook #{name} has been deprecated, use #{deprecated[name]}"
-
end
-
-
HOOKS[name].any? { |plugin| plugin[self, *args] }
-
end
-
-
##
-
# Add the supplied block to the available hooks, with the given
-
# name.
-
-
1
def self.add_hook name, &block
-
1
HOOKS[name] << block
-
end
-
-
1
add_hook :died do |at, err|
-
warn "Unhandled exception: #{err}"
-
warn err.backtrace.join("\n ")
-
warn "Quitting"
-
end
-
end
-
#--
-
# Copyright (c) 2004-2011 David Heinemeier Hansson
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining
-
# a copy of this software and associated documentation files (the
-
# "Software"), to deal in the Software without restriction, including
-
# without limitation the rights to use, copy, modify, merge, publish,
-
# distribute, sublicense, and/or sell copies of the Software, and to
-
# permit persons to whom the Software is furnished to do so, subject to
-
# the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be
-
# included in all copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
#++
-
-
1
actionpack_path = File.expand_path('../../../actionpack/lib', __FILE__)
-
1
$:.unshift(actionpack_path) if File.directory?(actionpack_path) && !$:.include?(actionpack_path)
-
-
1
require 'abstract_controller'
-
1
require 'action_view'
-
1
require 'action_mailer/version'
-
-
# Common Active Support usage in Action Mailer
-
1
require 'active_support/core_ext/class'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/array/uniq_by'
-
1
require 'active_support/core_ext/module/attr_internal'
-
1
require 'active_support/core_ext/module/delegation'
-
1
require 'active_support/core_ext/string/inflections'
-
1
require 'active_support/lazy_load_hooks'
-
-
1
module ActionMailer
-
1
extend ::ActiveSupport::Autoload
-
-
1
autoload :AdvAttrAccessor
-
1
autoload :Collector
-
1
autoload :Base
-
1
autoload :DeliveryMethods
-
1
autoload :MailHelper
-
1
autoload :OldApi
-
1
autoload :TestCase
-
1
autoload :TestHelper
-
end
-
1
require "action_mailer"
-
1
require "rails"
-
1
require "abstract_controller/railties/routes_helpers"
-
-
1
module ActionMailer
-
1
class Railtie < Rails::Railtie
-
1
config.action_mailer = ActiveSupport::OrderedOptions.new
-
-
1
initializer "action_mailer.logger" do
-
1
ActiveSupport.on_load(:action_mailer) { self.logger ||= Rails.logger }
-
end
-
-
1
initializer "action_mailer.set_configs" do |app|
-
1
paths = app.config.paths
-
1
options = app.config.action_mailer
-
-
1
options.assets_dir ||= paths["public"].first
-
1
options.javascripts_dir ||= paths["public/javascripts"].first
-
1
options.stylesheets_dir ||= paths["public/stylesheets"].first
-
-
# make sure readers methods get compiled
-
1
options.asset_path ||= app.config.asset_path
-
1
options.asset_host ||= app.config.asset_host
-
-
1
ActiveSupport.on_load(:action_mailer) do
-
include AbstractController::UrlFor
-
extend ::AbstractController::Railties::RoutesHelpers.with(app.routes)
-
include app.routes.mounted_helpers
-
-
register_interceptors(options.delete(:interceptors))
-
register_observers(options.delete(:observers))
-
-
options.each { |k,v| send("#{k}=", v) }
-
end
-
end
-
-
1
initializer "action_mailer.compile_config_methods" do
-
1
ActiveSupport.on_load(:action_mailer) do
-
config.compile_methods! if config.respond_to?(:compile_methods!)
-
end
-
end
-
end
-
end
-
1
module ActionMailer
-
1
module VERSION #:nodoc:
-
1
MAJOR = 3
-
1
MINOR = 1
-
1
TINY = 0
-
1
PRE = nil
-
-
1
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
-
end
-
end
-
1
activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
-
1
$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
-
-
1
require 'action_pack'
-
1
require 'active_support/concern'
-
1
require 'active_support/ruby/shim'
-
1
require 'active_support/dependencies/autoload'
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/module/attr_internal'
-
1
require 'active_support/core_ext/module/delegation'
-
1
require 'active_support/core_ext/module/anonymous'
-
1
require 'active_support/i18n'
-
-
1
module AbstractController
-
1
extend ActiveSupport::Autoload
-
-
1
autoload :Base
-
1
autoload :Callbacks
-
1
autoload :Collector
-
1
autoload :Helpers
-
1
autoload :Layouts
-
1
autoload :Logger
-
1
autoload :Rendering
-
1
autoload :Translation
-
1
autoload :AssetPaths
-
1
autoload :ViewPaths
-
1
autoload :UrlFor
-
end
-
1
module AbstractController
-
1
module AssetPaths
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
config_accessor :asset_host, :asset_path, :assets_dir, :javascripts_dir, :stylesheets_dir
-
end
-
end
-
end
-
1
require 'erubis'
-
1
require 'active_support/configurable'
-
1
require 'active_support/descendants_tracker'
-
1
require 'active_support/core_ext/module/anonymous'
-
-
1
module AbstractController
-
1
class Error < StandardError; end
-
1
class ActionNotFound < StandardError; end
-
-
# <tt>AbstractController::Base</tt> is a low-level API. Nobody should be
-
# using it directly, and subclasses (like ActionController::Base) are
-
# expected to provide their own +render+ method, since rendering means
-
# different things depending on the context.
-
1
class Base
-
1
attr_internal :response_body
-
1
attr_internal :action_name
-
1
attr_internal :formats
-
-
1
include ActiveSupport::Configurable
-
1
extend ActiveSupport::DescendantsTracker
-
-
1
undef_method :not_implemented
-
1
class << self
-
1
attr_reader :abstract
-
1
alias_method :abstract?, :abstract
-
-
# Define a controller as abstract. See internal_methods for more
-
# details.
-
1
def abstract!
-
3
@abstract = true
-
end
-
-
# A list of all internal methods for a controller. This finds the first
-
# abstract superclass of a controller, and gets a list of all public
-
# instance methods on that abstract class. Public instance methods of
-
# a controller would normally be considered action methods, so methods
-
# declared on abstract classes are being removed.
-
# (ActionController::Metal and ActionController::Base are defined as abstract)
-
1
def internal_methods
-
controller = self
-
controller = controller.superclass until controller.abstract?
-
controller.public_instance_methods(true)
-
end
-
-
# The list of hidden actions to an empty array. Defaults to an
-
# empty array. This can be modified by other modules or subclasses
-
# to specify particular actions as hidden.
-
#
-
# ==== Returns
-
# * <tt>array</tt> - An array of method names that should not be considered actions.
-
1
def hidden_actions
-
[]
-
end
-
-
# A list of method names that should be considered actions. This
-
# includes all public instance methods on a controller, less
-
# any internal methods (see #internal_methods), adding back in
-
# any methods that are internal, but still exist on the class
-
# itself. Finally, #hidden_actions are removed.
-
#
-
# ==== Returns
-
# * <tt>array</tt> - A list of all methods that should be considered actions.
-
1
def action_methods
-
@action_methods ||= begin
-
# All public instance methods of this class, including ancestors
-
methods = (public_instance_methods(true) -
-
# Except for public instance methods of Base and its ancestors
-
internal_methods +
-
# Be sure to include shadowed public instance methods of this class
-
public_instance_methods(false)).uniq.map { |x| x.to_s } -
-
# And always exclude explicitly hidden actions
-
hidden_actions.to_a
-
-
# Clear out AS callback method pollution
-
methods.reject { |method| method =~ /_one_time_conditions/ }
-
end
-
end
-
-
# action_methods are cached and there is sometimes need to refresh
-
# them. clear_action_methods! allows you to do that, so next time
-
# you run action_methods, they will be recalculated
-
1
def clear_action_methods!
-
135
@action_methods = nil
-
end
-
-
# Returns the full controller name, underscored, without the ending Controller.
-
# For instance, MyApp::MyPostsController would return "my_app/my_posts" for
-
# controller_name.
-
#
-
# ==== Returns
-
# * <tt>string</tt>
-
1
def controller_path
-
15
@controller_path ||= name.sub(/Controller$/, '').underscore unless anonymous?
-
end
-
-
1
def method_added(name)
-
135
super
-
135
clear_action_methods!
-
end
-
end
-
-
1
abstract!
-
-
# Calls the action going through the entire action dispatch stack.
-
#
-
# The actual method that is called is determined by calling
-
# #method_for_action. If no method can handle the action, then an
-
# ActionNotFound error is raised.
-
#
-
# ==== Returns
-
# * <tt>self</tt>
-
1
def process(action, *args)
-
@_action_name = action_name = action.to_s
-
-
unless action_name = method_for_action(action_name)
-
raise ActionNotFound, "The action '#{action}' could not be found for #{self.class.name}"
-
end
-
-
@_response_body = nil
-
-
process_action(action_name, *args)
-
end
-
-
# Delegates to the class' #controller_path
-
1
def controller_path
-
self.class.controller_path
-
end
-
-
1
def action_methods
-
self.class.action_methods
-
end
-
-
# Returns true if a method for the action is available and
-
# can be dispatched, false otherwise.
-
#
-
# Notice that <tt>action_methods.include?("foo")</tt> may return
-
# false and <tt>available_action?("foo")</tt> returns true because
-
# available action consider actions that are also available
-
# through other means, for example, implicit render ones.
-
1
def available_action?(action_name)
-
method_for_action(action_name).present?
-
end
-
-
1
private
-
-
# Returns true if the name can be considered an action because
-
# it has a method defined in the controller.
-
#
-
# ==== Parameters
-
# * <tt>name</tt> - The name of an action to be tested
-
#
-
# ==== Returns
-
# * <tt>TrueClass</tt>, <tt>FalseClass</tt>
-
#
-
# :api: private
-
1
def action_method?(name)
-
self.class.action_methods.include?(name)
-
end
-
-
# Call the action. Override this in a subclass to modify the
-
# behavior around processing an action. This, and not #process,
-
# is the intended way to override action dispatching.
-
#
-
# Notice that the first argument is the method to be dispatched
-
# which is *not* necessarily the same as the action name.
-
1
def process_action(method_name, *args)
-
send_action(method_name, *args)
-
end
-
-
# Actually call the method associated with the action. Override
-
# this method if you wish to change how action methods are called,
-
# not to add additional behavior around it. For example, you would
-
# override #send_action if you want to inject arguments into the
-
# method.
-
1
alias send_action send
-
-
# If the action name was not found, but a method called "action_missing"
-
# was found, #method_for_action will return "_handle_action_missing".
-
# This method calls #action_missing with the current action name.
-
1
def _handle_action_missing(*args)
-
action_missing(@_action_name, *args)
-
end
-
-
# Takes an action name and returns the name of the method that will
-
# handle the action. In normal cases, this method returns the same
-
# name as it receives. By default, if #method_for_action receives
-
# a name that is not an action, it will look for an #action_missing
-
# method and return "_handle_action_missing" if one is found.
-
#
-
# Subclasses may override this method to add additional conditions
-
# that should be considered an action. For instance, an HTTP controller
-
# with a template matching the action name is considered to exist.
-
#
-
# If you override this method to handle additional cases, you may
-
# also provide a method (like _handle_method_missing) to handle
-
# the case.
-
#
-
# If none of these conditions are true, and method_for_action
-
# returns nil, an ActionNotFound exception will be raised.
-
#
-
# ==== Parameters
-
# * <tt>action_name</tt> - An action name to find a method name for
-
#
-
# ==== Returns
-
# * <tt>string</tt> - The name of the method that handles the action
-
# * <tt>nil</tt> - No method name could be found. Raise ActionNotFound.
-
1
def method_for_action(action_name)
-
if action_method?(action_name) then action_name
-
elsif respond_to?(:action_missing, true) then "_handle_action_missing"
-
end
-
end
-
end
-
end
-
1
module AbstractController
-
1
module Callbacks
-
1
extend ActiveSupport::Concern
-
-
# Uses ActiveSupport::Callbacks as the base functionality. For
-
# more details on the whole callback system, read the documentation
-
# for ActiveSupport::Callbacks.
-
1
include ActiveSupport::Callbacks
-
-
1
included do
-
1
define_callbacks :process_action, :terminator => "response_body"
-
end
-
-
# Override AbstractController::Base's process_action to run the
-
# process_action callbacks around the normal behavior.
-
1
def process_action(*args)
-
run_callbacks(:process_action, action_name) do
-
super
-
end
-
end
-
-
1
module ClassMethods
-
# If :only or :except are used, convert the options into the
-
# primitive form (:per_key) used by ActiveSupport::Callbacks.
-
# The basic idea is that :only => :index gets converted to
-
# :if => proc {|c| c.action_name == "index" }, but that the
-
# proc is only evaluated once per action for the lifetime of
-
# a Rails process.
-
#
-
# ==== Options
-
# * <tt>only</tt> - The callback should be run only for this action
-
# * <tt>except</tt> - The callback should be run for all actions except this action
-
1
def _normalize_callback_options(options)
-
1
if only = options[:only]
-
only = Array(only).map {|o| "action_name == '#{o}'"}.join(" || ")
-
options[:per_key] = {:if => only}
-
end
-
1
if except = options[:except]
-
except = Array(except).map {|e| "action_name == '#{e}'"}.join(" || ")
-
options[:per_key] = {:unless => except}
-
end
-
end
-
-
# Skip before, after, and around filters matching any of the names
-
#
-
# ==== Parameters
-
# * <tt>names</tt> - A list of valid names that could be used for
-
# callbacks. Note that skipping uses Ruby equality, so it's
-
# impossible to skip a callback defined using an anonymous proc
-
# using #skip_filter
-
1
def skip_filter(*names, &blk)
-
skip_before_filter(*names)
-
skip_after_filter(*names)
-
skip_around_filter(*names)
-
end
-
-
# Take callback names and an optional callback proc, normalize them,
-
# then call the block with each callback. This allows us to abstract
-
# the normalization across several methods that use it.
-
#
-
# ==== Parameters
-
# * <tt>callbacks</tt> - An array of callbacks, with an optional
-
# options hash as the last parameter.
-
# * <tt>block</tt> - A proc that should be added to the callbacks.
-
#
-
# ==== Block Parameters
-
# * <tt>name</tt> - The callback to be added
-
# * <tt>options</tt> - A hash of options to be used when adding the callback
-
1
def _insert_callbacks(callbacks, block)
-
1
options = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
-
1
_normalize_callback_options(options)
-
1
callbacks.push(block) if block
-
1
callbacks.each do |callback|
-
1
yield callback, options
-
end
-
end
-
-
# set up before_filter, prepend_before_filter, skip_before_filter, etc.
-
# for each of before, after, and around.
-
1
[:before, :after, :around].each do |filter|
-
3
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
# Append a before, after or around filter. See _insert_callbacks
-
# for details on the allowed parameters.
-
def #{filter}_filter(*names, &blk)
-
_insert_callbacks(names, blk) do |name, options|
-
options[:if] = (Array.wrap(options[:if]) << "!halted") if #{filter == :after}
-
set_callback(:process_action, :#{filter}, name, options)
-
end
-
end
-
-
# Prepend a before, after or around filter. See _insert_callbacks
-
# for details on the allowed parameters.
-
def prepend_#{filter}_filter(*names, &blk)
-
_insert_callbacks(names, blk) do |name, options|
-
options[:if] = (Array.wrap(options[:if]) << "!halted") if #{filter == :after}
-
set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true))
-
end
-
end
-
-
# Skip a before, after or around filter. See _insert_callbacks
-
# for details on the allowed parameters.
-
def skip_#{filter}_filter(*names, &blk)
-
_insert_callbacks(names, blk) do |name, options|
-
skip_callback(:process_action, :#{filter}, name, options)
-
end
-
end
-
-
# *_filter is the same as append_*_filter
-
alias_method :append_#{filter}_filter, :#{filter}_filter
-
RUBY_EVAL
-
end
-
end
-
end
-
end
-
1
require "action_dispatch/http/mime_type"
-
-
1
module AbstractController
-
1
module Collector
-
1
def self.generate_method_for_mime(mime)
-
13
sym = mime.is_a?(Symbol) ? mime : mime.to_sym
-
13
const = sym.to_s.upcase
-
13
class_eval <<-RUBY, __FILE__, __LINE__ + 1
-
def #{sym}(*args, &block) # def html(*args, &block)
-
custom(Mime::#{const}, *args, &block) # custom(Mime::HTML, *args, &block)
-
end # end
-
RUBY
-
end
-
-
1
Mime::SET.each do |mime|
-
13
generate_method_for_mime(mime)
-
end
-
-
1
protected
-
-
1
def method_missing(symbol, &block)
-
mime_constant = Mime.const_get(symbol.to_s.upcase)
-
-
if Mime::SET.include?(mime_constant)
-
AbstractController::Collector.generate_method_for_mime(mime_constant)
-
send(symbol, &block)
-
else
-
super
-
end
-
end
-
end
-
end
-
1
require 'active_support/dependencies'
-
-
1
module AbstractController
-
1
module Helpers
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
class_attribute :_helpers
-
1
self._helpers = Module.new
-
-
1
class_attribute :_helper_methods
-
1
self._helper_methods = Array.new
-
end
-
-
1
module ClassMethods
-
# When a class is inherited, wrap its helper module in a new module.
-
# This ensures that the parent class's module can be changed
-
# independently of the child class's.
-
1
def inherited(klass)
-
4
helpers = _helpers
-
8
klass._helpers = Module.new { include helpers }
-
8
klass.class_eval { default_helper_module! unless anonymous? }
-
4
super
-
end
-
-
# Declare a controller method as a helper. For example, the following
-
# makes the +current_user+ controller method available to the view:
-
# class ApplicationController < ActionController::Base
-
# helper_method :current_user, :logged_in?
-
#
-
# def current_user
-
# @current_user ||= User.find_by_id(session[:user])
-
# end
-
#
-
# def logged_in?
-
# current_user != nil
-
# end
-
# end
-
#
-
# In a view:
-
# <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%>
-
#
-
# ==== Parameters
-
# * <tt>method[, method]</tt> - A name or names of a method on the controller
-
# to be made available on the view.
-
1
def helper_method(*meths)
-
4
meths.flatten!
-
4
self._helper_methods += meths
-
-
4
meths.each do |meth|
-
5
_helpers.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
-
def #{meth}(*args, &blk)
-
controller.send(%(#{meth}), *args, &blk)
-
end
-
ruby_eval
-
end
-
end
-
-
# The +helper+ class method can take a series of helper module names, a block, or both.
-
#
-
# ==== Parameters
-
# * <tt>*args</tt> - Module, Symbol, String, :all
-
# * <tt>block</tt> - A block defining helper methods
-
#
-
# ==== Examples
-
# When the argument is a module it will be included directly in the template class.
-
# helper FooHelper # => includes FooHelper
-
#
-
# When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file
-
# and include the module in the template class. The second form illustrates how to include custom helpers
-
# when working with namespaced controllers, or other cases where the file containing the helper definition is not
-
# in one of Rails' standard load paths:
-
# helper :foo # => requires 'foo_helper' and includes FooHelper
-
# helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper
-
#
-
# Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available
-
# to the template.
-
#
-
# # One line
-
# helper { def hello() "Hello, world!" end }
-
#
-
# # Multi-line
-
# helper do
-
# def foo(bar)
-
# "#{bar} is the very best"
-
# end
-
# end
-
#
-
# Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of
-
# +symbols+, +strings+, +modules+ and blocks.
-
#
-
# helper(:three, BlindHelper) { def mice() 'mice' end }
-
#
-
1
def helper(*args, &block)
-
5
modules_for_helpers(args).each do |mod|
-
8
add_template_helper(mod)
-
end
-
-
5
_helpers.module_eval(&block) if block_given?
-
end
-
-
# Clears up all existing helpers in this class, only keeping the helper
-
# with the same name as this class.
-
1
def clear_helpers
-
inherited_helper_methods = _helper_methods
-
self._helpers = Module.new
-
self._helper_methods = Array.new
-
-
inherited_helper_methods.each { |meth| helper_method meth }
-
default_helper_module! unless anonymous?
-
end
-
-
# Returns a list of modules, normalized from the acceptable kinds of
-
# helpers with the following behavior:
-
#
-
# String or Symbol:: :FooBar or "FooBar" becomes "foo_bar_helper",
-
# and "foo_bar_helper.rb" is loaded using require_dependency.
-
#
-
# Module:: No further processing
-
#
-
# After loading the appropriate files, the corresponding modules
-
# are returned.
-
#
-
# ==== Parameters
-
# * <tt>args</tt> - An array of helpers
-
#
-
# ==== Returns
-
# * <tt>Array</tt> - A normalized list of modules for the list of
-
# helpers provided.
-
1
def modules_for_helpers(args)
-
5
args.flatten.map! do |arg|
-
8
case arg
-
when String, Symbol
-
8
file_name = "#{arg.to_s.underscore}_helper"
-
8
require_dependency(file_name, "Missing helper file helpers/%s.rb")
-
8
file_name.camelize.constantize
-
when Module
-
arg
-
else
-
raise ArgumentError, "helper must be a String, Symbol, or Module"
-
end
-
end
-
end
-
-
1
private
-
# Makes all the (instance) methods in the helper module available to templates
-
# rendered through this controller.
-
#
-
# ==== Parameters
-
# * <tt>module</tt> - The module to include into the current helper module
-
# for the class
-
1
def add_template_helper(mod)
-
16
_helpers.module_eval { include mod }
-
end
-
-
1
def default_helper_module!
-
4
module_name = name.sub(/Controller$/, '')
-
4
module_path = module_name.underscore
-
4
helper module_path
-
rescue MissingSourceFile => e
-
raise e unless e.is_missing? "helpers/#{module_path}_helper"
-
rescue NameError => e
-
raise e unless e.missing_name? "#{module_name}Helper"
-
end
-
end
-
end
-
end
-
1
require "active_support/core_ext/module/remove_method"
-
-
1
module AbstractController
-
# Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
-
# repeated setups. The inclusion pattern has pages that look like this:
-
#
-
# <%= render "shared/header" %>
-
# Hello World
-
# <%= render "shared/footer" %>
-
#
-
# This approach is a decent way of keeping common structures isolated from the changing content, but it's verbose
-
# and if you ever want to change the structure of these two includes, you'll have to change all the templates.
-
#
-
# With layouts, you can flip it around and have the common structure know where to insert changing content. This means
-
# that the header and footer are only mentioned in one place, like this:
-
#
-
# // The header part of this layout
-
# <%= yield %>
-
# // The footer part of this layout
-
#
-
# And then you have content pages that look like this:
-
#
-
# hello world
-
#
-
# At rendering time, the content page is computed and then inserted in the layout, like this:
-
#
-
# // The header part of this layout
-
# hello world
-
# // The footer part of this layout
-
#
-
# == Accessing shared variables
-
#
-
# Layouts have access to variables specified in the content pages and vice versa. This allows you to have layouts with
-
# references that won't materialize before rendering time:
-
#
-
# <h1><%= @page_title %></h1>
-
# <%= yield %>
-
#
-
# ...and content pages that fulfill these references _at_ rendering time:
-
#
-
# <% @page_title = "Welcome" %>
-
# Off-world colonies offers you a chance to start a new life
-
#
-
# The result after rendering is:
-
#
-
# <h1>Welcome</h1>
-
# Off-world colonies offers you a chance to start a new life
-
#
-
# == Layout assignment
-
#
-
# You can either specify a layout declaratively (using the #layout class method) or give
-
# it the same name as your controller, and place it in <tt>app/views/layouts</tt>.
-
# If a subclass does not have a layout specified, it inherits its layout using normal Ruby inheritance.
-
#
-
# For instance, if you have PostsController and a template named <tt>app/views/layouts/posts.html.erb</tt>,
-
# that template will be used for all actions in PostsController and controllers inheriting
-
# from PostsController.
-
#
-
# If you use a module, for instance Weblog::PostsController, you will need a template named
-
# <tt>app/views/layouts/weblog/posts.html.erb</tt>.
-
#
-
# Since all your controllers inherit from ApplicationController, they will use
-
# <tt>app/views/layouts/application.html.erb</tt> if no other layout is specified
-
# or provided.
-
#
-
# == Inheritance Examples
-
#
-
# class BankController < ActionController::Base
-
# layout "bank_standard"
-
#
-
# class InformationController < BankController
-
#
-
# class TellerController < BankController
-
# # teller.html.erb exists
-
#
-
# class TillController < TellerController
-
#
-
# class VaultController < BankController
-
# layout :access_level_layout
-
#
-
# class EmployeeController < BankController
-
# layout nil
-
#
-
# In these examples:
-
# * The InformationController uses the "bank_standard" layout, inherited from BankController.
-
# * The TellerController follows convention and uses +app/views/layouts/teller.html.erb+.
-
# * The TillController inherits the layout from TellerController and uses +teller.html.erb+ as well.
-
# * The VaultController chooses a layout dynamically by calling the <tt>access_level_layout</tt> method.
-
# * The EmployeeController does not use a layout at all.
-
#
-
# == Types of layouts
-
#
-
# Layouts are basically just regular templates, but the name of this template needs not be specified statically. Sometimes
-
# you want to alternate layouts depending on runtime information, such as whether someone is logged in or not. This can
-
# be done either by specifying a method reference as a symbol or using an inline method (as a proc).
-
#
-
# The method reference is the preferred approach to variable layouts and is used like this:
-
#
-
# class WeblogController < ActionController::Base
-
# layout :writers_and_readers
-
#
-
# def index
-
# # fetching posts
-
# end
-
#
-
# private
-
# def writers_and_readers
-
# logged_in? ? "writer_layout" : "reader_layout"
-
# end
-
#
-
# Now when a new request for the index action is processed, the layout will vary depending on whether the person accessing
-
# is logged in or not.
-
#
-
# If you want to use an inline method, such as a proc, do something like this:
-
#
-
# class WeblogController < ActionController::Base
-
# layout proc{ |controller| controller.logged_in? ? "writer_layout" : "reader_layout" }
-
# end
-
#
-
# Of course, the most common way of specifying a layout is still just as a plain template name:
-
#
-
# class WeblogController < ActionController::Base
-
# layout "weblog_standard"
-
# end
-
#
-
# If no directory is specified for the template name, the template will by default be looked for in <tt>app/views/layouts/</tt>.
-
# Otherwise, it will be looked up relative to the template root.
-
#
-
# == Conditional layouts
-
#
-
# If you have a layout that by default is applied to all the actions of a controller, you still have the option of rendering
-
# a given action or set of actions without a layout, or restricting a layout to only a single action or a set of actions. The
-
# <tt>:only</tt> and <tt>:except</tt> options can be passed to the layout call. For example:
-
#
-
# class WeblogController < ActionController::Base
-
# layout "weblog_standard", :except => :rss
-
#
-
# # ...
-
#
-
# end
-
#
-
# This will assign "weblog_standard" as the WeblogController's layout for all actions except for the +rss+ action, which will
-
# be rendered directly, without wrapping a layout around the rendered view.
-
#
-
# Both the <tt>:only</tt> and <tt>:except</tt> condition can accept an arbitrary number of method references, so
-
# #<tt>:except => [ :rss, :text_only ]</tt> is valid, as is <tt>:except => :rss</tt>.
-
#
-
# == Using a different layout in the action render call
-
#
-
# If most of your actions use the same layout, it makes perfect sense to define a controller-wide layout as described above.
-
# Sometimes you'll have exceptions where one action wants to use a different layout than the rest of the controller.
-
# You can do this by passing a <tt>:layout</tt> option to the <tt>render</tt> call. For example:
-
#
-
# class WeblogController < ActionController::Base
-
# layout "weblog_standard"
-
#
-
# def help
-
# render :action => "help", :layout => "help"
-
# end
-
# end
-
#
-
# This will override the controller-wide "weblog_standard" layout, and will render the help action with the "help" layout instead.
-
1
module Layouts
-
1
extend ActiveSupport::Concern
-
-
1
include Rendering
-
-
1
included do
-
1
class_attribute :_layout_conditions
-
1
remove_possible_method :_layout_conditions
-
1
delegate :_layout_conditions, :to => :'self.class'
-
1
self._layout_conditions = {}
-
1
_write_layout_method
-
end
-
-
1
module ClassMethods
-
1
def inherited(klass)
-
4
super
-
4
klass._write_layout_method
-
end
-
-
# This module is mixed in if layout conditions are provided. This means
-
# that if no layout conditions are used, this method is not used
-
1
module LayoutConditions
-
# Determines whether the current action has a layout by checking the
-
# action name against the :only and :except conditions set on the
-
# layout.
-
#
-
# ==== Returns
-
# * <tt> Boolean</tt> - True if the action has a layout, false otherwise.
-
1
def action_has_layout?
-
return unless super
-
-
conditions = _layout_conditions
-
-
if only = conditions[:only]
-
only.include?(action_name)
-
elsif except = conditions[:except]
-
!except.include?(action_name)
-
else
-
true
-
end
-
end
-
end
-
-
# Specify the layout to use for this class.
-
#
-
# If the specified layout is a:
-
# String:: the String is the template name
-
# Symbol:: call the method specified by the symbol, which will return
-
# the template name
-
# false:: There is no layout
-
# true:: raise an ArgumentError
-
#
-
# ==== Parameters
-
# * <tt>String, Symbol, false</tt> - The layout to use.
-
#
-
# ==== Options (conditions)
-
# * :only - A list of actions to apply this layout to.
-
# * :except - Apply this layout to all actions but this one.
-
1
def layout(layout, conditions = {})
-
include LayoutConditions unless conditions.empty?
-
-
conditions.each {|k, v| conditions[k] = Array(v).map {|a| a.to_s} }
-
self._layout_conditions = conditions
-
-
@_layout = layout || false # Converts nil to false
-
_write_layout_method
-
end
-
-
# If no layout is supplied, look for a template named the return
-
# value of this method.
-
#
-
# ==== Returns
-
# * <tt>String</tt> - A template name
-
1
def _implied_layout_name
-
15
controller_path
-
end
-
-
# Creates a _layout method to be called by _default_layout .
-
#
-
# If a layout is not explicitly mentioned then look for a layout with the controller's name.
-
# if nothing is found then try same procedure to find super class's layout.
-
1
def _write_layout_method
-
5
remove_possible_method(:_layout)
-
-
5
case defined?(@_layout) ? @_layout : nil
-
when String
-
self.class_eval %{def _layout; #{@_layout.inspect} end}, __FILE__, __LINE__
-
when Symbol
-
self.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
-
def _layout
-
#{@_layout}.tap do |layout|
-
unless layout.is_a?(String) || !layout
-
raise ArgumentError, "Your layout method :#{@_layout} returned \#{layout}. It " \
-
"should have returned a String, false, or nil"
-
end
-
end
-
end
-
ruby_eval
-
when Proc
-
define_method :_layout_from_proc, &@_layout
-
self.class_eval %{def _layout; _layout_from_proc(self) end}, __FILE__, __LINE__
-
when false
-
self.class_eval %{def _layout; end}, __FILE__, __LINE__
-
when true
-
raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil"
-
when nil
-
5
if name
-
5
_prefixes = _implied_layout_name =~ /\blayouts/ ? [] : ["layouts"]
-
-
5
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
-
def _layout
-
if template_exists?("#{_implied_layout_name}", #{_prefixes.inspect})
-
"#{_implied_layout_name}"
-
else
-
super
-
end
-
end
-
RUBY
-
end
-
end
-
10
self.class_eval { private :_layout }
-
end
-
end
-
-
1
def _normalize_options(options)
-
super
-
-
if _include_layout?(options)
-
layout = options.key?(:layout) ? options.delete(:layout) : :default
-
value = _layout_for_option(layout)
-
options[:layout] = (value =~ /\blayouts/ ? value : "layouts/#{value}") if value
-
end
-
end
-
-
1
attr_internal_writer :action_has_layout
-
-
1
def initialize(*)
-
@_action_has_layout = true
-
super
-
end
-
-
1
def action_has_layout?
-
@_action_has_layout
-
end
-
-
1
private
-
-
# This will be overwritten by _write_layout_method
-
1
def _layout; end
-
-
# Determine the layout for a given name and details, taking into account
-
# the name type.
-
#
-
# ==== Parameters
-
# * <tt>name</tt> - The name of the template
-
# * <tt>details</tt> - A list of details to restrict
-
# the lookup to. By default, layout lookup is limited to the
-
# formats specified for the current request.
-
1
def _layout_for_option(name)
-
case name
-
when String then name
-
when true then _default_layout(true)
-
when :default then _default_layout(false)
-
when false, nil then nil
-
else
-
raise ArgumentError,
-
"String, true, or false, expected for `layout'; you passed #{name.inspect}"
-
end
-
end
-
-
# Returns the default layout for this controller and a given set of details.
-
# Optionally raises an exception if the layout could not be found.
-
#
-
# ==== Parameters
-
# * <tt>details</tt> - A list of details to restrict the search by. This
-
# might include details like the format or locale of the template.
-
# * <tt>require_layout</tt> - If this is true, raise an ArgumentError
-
# with details about the fact that the exception could not be
-
# found (defaults to false)
-
#
-
# ==== Returns
-
# * <tt>template</tt> - The template object for the default layout (or nil)
-
1
def _default_layout(require_layout = false)
-
begin
-
layout_name = _layout if action_has_layout?
-
rescue NameError => e
-
raise e, "Could not render layout: #{e.message}"
-
end
-
-
if require_layout && action_has_layout? && !layout_name
-
raise ArgumentError,
-
"There was no default layout for #{self.class} in #{view_paths.inspect}"
-
end
-
-
layout_name
-
end
-
-
1
def _include_layout?(options)
-
(options.keys & [:text, :inline, :partial]).empty? || options.key?(:layout)
-
end
-
end
-
end
-
1
require "active_support/core_ext/logger"
-
1
require "active_support/benchmarkable"
-
-
1
module AbstractController
-
1
module Logger
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
config_accessor :logger
-
1
extend ActiveSupport::Benchmarkable
-
end
-
end
-
end
-
1
module AbstractController
-
1
module Railties
-
1
module RoutesHelpers
-
1
def self.with(routes)
-
1
Module.new do
-
1
define_method(:inherited) do |klass|
-
4
super(klass)
-
8
if namespace = klass.parents.detect {|m| m.respond_to?(:_railtie) }
-
klass.send(:include, namespace._railtie.routes.url_helpers)
-
else
-
4
klass.send(:include, routes.url_helpers)
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require "abstract_controller/base"
-
1
require "action_view"
-
1
require "active_support/core_ext/object/instance_variables"
-
-
1
module AbstractController
-
1
class DoubleRenderError < Error
-
1
DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"."
-
-
1
def initialize(message = nil)
-
super(message || DEFAULT_MESSAGE)
-
end
-
end
-
-
# This is a class to fix I18n global state. Whenever you provide I18n.locale during a request,
-
# it will trigger the lookup_context and consequently expire the cache.
-
1
class I18nProxy < ::I18n::Config #:nodoc:
-
1
attr_reader :original_config, :lookup_context
-
-
1
def initialize(original_config, lookup_context)
-
original_config = original_config.original_config if original_config.respond_to?(:original_config)
-
@original_config, @lookup_context = original_config, lookup_context
-
end
-
-
1
def locale
-
@original_config.locale
-
end
-
-
1
def locale=(value)
-
@lookup_context.locale = value
-
end
-
end
-
-
1
module Rendering
-
1
extend ActiveSupport::Concern
-
1
include AbstractController::ViewPaths
-
-
1
included do
-
1
config_accessor :protected_instance_variables, :instance_reader => false
-
1
self.protected_instance_variables = []
-
end
-
-
# Overwrite process to setup I18n proxy.
-
1
def process(*) #:nodoc:
-
old_config, I18n.config = I18n.config, I18nProxy.new(I18n.config, lookup_context)
-
super
-
ensure
-
I18n.config = old_config
-
end
-
-
1
module ClassMethods
-
1
def view_context_class
-
@view_context_class ||= begin
-
routes = _routes if respond_to?(:_routes)
-
helpers = _helpers if respond_to?(:_helpers)
-
ActionView::Base.prepare(routes, helpers)
-
end
-
end
-
end
-
-
1
attr_internal_writer :view_context_class
-
-
# Explicitly define protected_instance_variables so it can be
-
# inherited and overwritten by other modules if needed.
-
1
def protected_instance_variables
-
config.protected_instance_variables
-
end
-
-
1
def view_context_class
-
@_view_context_class || self.class.view_context_class
-
end
-
-
1
def initialize(*)
-
@_view_context_class = nil
-
super
-
end
-
-
# An instance of a view class. The default view class is ActionView::Base
-
#
-
# The view class must have the following methods:
-
# View.new[lookup_context, assigns, controller]
-
# Create a new ActionView instance for a controller
-
# View#render[options]
-
# Returns String with the rendered template
-
#
-
# Override this method in a module to change the default behavior.
-
1
def view_context
-
view_context_class.new(view_renderer, view_assigns, self)
-
end
-
-
# Returns an object that is able to render templates.
-
1
def view_renderer
-
@_view_renderer ||= ActionView::Renderer.new(lookup_context)
-
end
-
-
# Normalize arguments, options and then delegates render_to_body and
-
# sticks the result in self.response_body.
-
1
def render(*args, &block)
-
options = _normalize_render(*args, &block)
-
self.response_body = render_to_body(options)
-
end
-
-
# Raw rendering of a template to a string. Just convert the results of
-
# render_response into a String.
-
# :api: plugin
-
1
def render_to_string(*args, &block)
-
options = _normalize_render(*args, &block)
-
render_to_body(options)
-
end
-
-
# Raw rendering of a template to a Rack-compatible body.
-
# :api: plugin
-
1
def render_to_body(options = {})
-
_process_options(options)
-
_render_template(options)
-
end
-
-
# Find and renders a template based on the options given.
-
# :api: private
-
1
def _render_template(options) #:nodoc:
-
view_renderer.render(view_context, options)
-
end
-
-
1
private
-
-
1
DEFAULT_PROTECTED_INSTANCE_VARIABLES = %w(
-
@_action_name @_response_body @_formats @_prefixes @_config
-
@_view_context_class @_view_renderer @_lookup_context
-
)
-
-
# This method should return a hash with assigns.
-
# You can overwrite this configuration per controller.
-
# :api: public
-
1
def view_assigns
-
hash = {}
-
variables = instance_variable_names
-
variables -= protected_instance_variables
-
variables -= DEFAULT_PROTECTED_INSTANCE_VARIABLES
-
variables.each { |name| hash[name.to_s[1, name.length]] = instance_variable_get(name) }
-
hash
-
end
-
-
# Normalize args and options.
-
# :api: private
-
1
def _normalize_render(*args, &block)
-
options = _normalize_args(*args, &block)
-
_normalize_options(options)
-
options
-
end
-
-
# Normalize args by converting render "foo" to render :action => "foo" and
-
# render "foo/bar" to render :file => "foo/bar".
-
# :api: plugin
-
1
def _normalize_args(action=nil, options={})
-
case action
-
when NilClass
-
when Hash
-
options = action
-
when String, Symbol
-
action = action.to_s
-
key = action.include?(?/) ? :file : :action
-
options[key] = action
-
else
-
options[:partial] = action
-
end
-
-
options
-
end
-
-
# Normalize options.
-
# :api: plugin
-
1
def _normalize_options(options)
-
if options[:partial] == true
-
options[:partial] = action_name
-
end
-
-
if (options.keys & [:partial, :file, :template]).empty?
-
options[:prefixes] ||= _prefixes
-
end
-
-
options[:template] ||= (options[:action] || action_name).to_s
-
options
-
end
-
-
# Process extra options.
-
# :api: plugin
-
1
def _process_options(options)
-
end
-
end
-
end
-
1
module AbstractController
-
1
module Translation
-
1
def translate(*args)
-
I18n.translate(*args)
-
end
-
1
alias :t :translate
-
-
1
def localize(*args)
-
I18n.localize(*args)
-
end
-
1
alias :l :localize
-
end
-
end
-
# Includes +url_for+ into the host class (e.g. an abstract controller or mailer). The class
-
# has to provide a +RouteSet+ by implementing the <tt>_routes</tt> methods. Otherwise, an
-
# exception will be raised.
-
#
-
# Note that this module is completely decoupled from HTTP - the only requirement is a valid
-
# <tt>_routes</tt> implementation.
-
1
module AbstractController
-
1
module UrlFor
-
1
extend ActiveSupport::Concern
-
1
include ActionDispatch::Routing::UrlFor
-
-
1
def _routes
-
raise "In order to use #url_for, you must include routing helpers explicitly. " \
-
"For instance, `include Rails.application.routes.url_helpers"
-
end
-
-
1
module ClassMethods
-
1
def _routes
-
nil
-
end
-
-
1
def action_methods
-
@action_methods ||= begin
-
if _routes
-
super - _routes.named_routes.helper_names
-
else
-
super
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module AbstractController
-
1
module ViewPaths
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
class_attribute :_view_paths
-
1
self._view_paths = ActionView::PathSet.new
-
1
self._view_paths.freeze
-
end
-
-
1
delegate :find_template, :template_exists?, :view_paths, :formats, :formats=,
-
:locale, :locale=, :to => :lookup_context
-
-
1
module ClassMethods
-
1
def parent_prefixes
-
@parent_prefixes ||= begin
-
parent_controller = superclass
-
prefixes = []
-
-
until parent_controller.abstract?
-
prefixes << parent_controller.controller_path
-
parent_controller = parent_controller.superclass
-
end
-
-
prefixes
-
end
-
end
-
end
-
-
# The prefixes used in render "foo" shortcuts.
-
1
def _prefixes
-
@_prefixes ||= begin
-
parent_prefixes = self.class.parent_prefixes
-
parent_prefixes.dup.unshift(controller_path)
-
end
-
end
-
-
# LookupContext is the object responsible to hold all information required to lookup
-
# templates, i.e. view paths and details. Check ActionView::LookupContext for more
-
# information.
-
1
def lookup_context
-
@_lookup_context ||=
-
ActionView::LookupContext.new(self.class._view_paths, details_for_lookup, _prefixes)
-
end
-
-
1
def details_for_lookup
-
{ }
-
end
-
-
1
def append_view_path(path)
-
lookup_context.view_paths.push(*path)
-
end
-
-
1
def prepend_view_path(path)
-
lookup_context.view_paths.unshift(*path)
-
end
-
-
1
module ClassMethods
-
# Append a path to the list of view paths for this controller.
-
#
-
# ==== Parameters
-
# * <tt>path</tt> - If a String is provided, it gets converted into
-
# the default view path. You may also provide a custom view path
-
# (see ActionView::PathSet for more information)
-
1
def append_view_path(path)
-
self.view_paths = view_paths.dup + Array(path)
-
end
-
-
# Prepend a path to the list of view paths for this controller.
-
#
-
# ==== Parameters
-
# * <tt>path</tt> - If a String is provided, it gets converted into
-
# the default view path. You may also provide a custom view path
-
# (see ActionView::PathSet for more information)
-
1
def prepend_view_path(path)
-
1
self.view_paths = Array(path) + view_paths.dup
-
end
-
-
# A list of all of the default view paths for this controller.
-
1
def view_paths
-
1
_view_paths
-
end
-
-
# Set the view paths.
-
#
-
# ==== Parameters
-
# * <tt>paths</tt> - If a PathSet is provided, use that;
-
# otherwise, process the parameter into a PathSet.
-
1
def view_paths=(paths)
-
1
self._view_paths = ActionView::Base.process_view_paths(paths)
-
1
self._view_paths.freeze
-
end
-
end
-
end
-
end
-
1
require 'abstract_controller'
-
1
require 'action_dispatch'
-
-
1
module ActionController
-
1
extend ActiveSupport::Autoload
-
-
1
autoload :Base
-
1
autoload :Caching
-
1
autoload :Metal
-
1
autoload :Middleware
-
-
1
autoload_under "metal" do
-
1
autoload :Compatibility
-
1
autoload :ConditionalGet
-
1
autoload :Cookies
-
1
autoload :DataStreaming
-
1
autoload :Flash
-
1
autoload :ForceSSL
-
1
autoload :Head
-
1
autoload :Helpers
-
1
autoload :HideActions
-
1
autoload :HttpAuthentication
-
1
autoload :ImplicitRender
-
1
autoload :Instrumentation
-
1
autoload :MimeResponds
-
1
autoload :ParamsWrapper
-
1
autoload :RackDelegation
-
1
autoload :Redirecting
-
1
autoload :Renderers
-
1
autoload :Rendering
-
1
autoload :RequestForgeryProtection
-
1
autoload :Rescue
-
1
autoload :Responder
-
1
autoload :SessionManagement
-
1
autoload :Streaming
-
1
autoload :Testing
-
1
autoload :UrlFor
-
end
-
-
1
autoload :Integration, 'action_controller/deprecated/integration_test'
-
1
autoload :IntegrationTest, 'action_controller/deprecated/integration_test'
-
1
autoload :PerformanceTest, 'action_controller/deprecated/performance_test'
-
1
autoload :UrlWriter, 'action_controller/deprecated'
-
1
autoload :Routing, 'action_controller/deprecated'
-
1
autoload :TestCase, 'action_controller/test_case'
-
1
autoload :TemplateAssertions, 'action_controller/test_case'
-
-
1
eager_autoload do
-
1
autoload :RecordIdentifier
-
-
# TODO: Don't autoload exceptions, setup explicit
-
# requires for files that need them
-
1
autoload_at "action_controller/metal/exceptions" do
-
1
autoload :ActionControllerError
-
1
autoload :RenderError
-
1
autoload :RoutingError
-
1
autoload :MethodNotAllowed
-
1
autoload :NotImplemented
-
1
autoload :UnknownController
-
1
autoload :MissingFile
-
1
autoload :RenderError
-
1
autoload :SessionOverflowError
-
1
autoload :UnknownHttpMethod
-
end
-
end
-
end
-
-
# All of these simply register additional autoloads
-
1
require 'action_view'
-
1
require 'action_controller/vendor/html-scanner'
-
-
# Common Active Support usage in Action Controller
-
1
require 'active_support/concern'
-
1
require 'active_support/core_ext/class/attribute_accessors'
-
1
require 'active_support/core_ext/load_error'
-
1
require 'active_support/core_ext/module/attr_internal'
-
1
require 'active_support/core_ext/module/delegation'
-
1
require 'active_support/core_ext/name_error'
-
1
require 'active_support/core_ext/uri'
-
1
require 'active_support/inflector'
-
1
require "action_controller/log_subscriber"
-
-
1
module ActionController
-
# Action Controllers are the core of a web request in \Rails. They are made up of one or more actions that are executed
-
# on request and then either render a template or redirect to another action. An action is defined as a public method
-
# on the controller, which will automatically be made accessible to the web-server through \Rails Routes.
-
#
-
# By default, only the ApplicationController in a \Rails application inherits from <tt>ActionController::Base</tt>. All other
-
# controllers in turn inherit from ApplicationController. This gives you one class to configure things such as
-
# request forgery protection and filtering of sensitive request parameters.
-
#
-
# A sample controller could look like this:
-
#
-
# class PostsController < ApplicationController
-
# def index
-
# @posts = Post.all
-
# end
-
#
-
# def create
-
# @post = Post.create params[:post]
-
# redirect_to posts_path
-
# end
-
# end
-
#
-
# Actions, by default, render a template in the <tt>app/views</tt> directory corresponding to the name of the controller and action
-
# after executing code in the action. For example, the +index+ action of the PostsController would render the
-
# template <tt>app/views/posts/index.html.erb</tt> by default after populating the <tt>@posts</tt> instance variable.
-
#
-
# Unlike index, the create action will not render a template. After performing its main purpose (creating a
-
# new post), it initiates a redirect instead. This redirect works by returning an external
-
# "302 Moved" HTTP response that takes the user to the index action.
-
#
-
# These two methods represent the two basic action archetypes used in Action Controllers. Get-and-show and do-and-redirect.
-
# Most actions are variations on these themes.
-
#
-
# == Requests
-
#
-
# For every request, the router determines the value of the +controller+ and +action+ keys. These determine which controller
-
# and action are called. The remaining request parameters, the session (if one is available), and the full request with
-
# all the HTTP headers are made available to the action through accessor methods. Then the action is performed.
-
#
-
# The full request object is available via the request accessor and is primarily used to query for HTTP headers:
-
#
-
# def server_ip
-
# location = request.env["SERVER_ADDR"]
-
# render :text => "This server hosted at #{location}"
-
# end
-
#
-
# == Parameters
-
#
-
# All request parameters, whether they come from a GET or POST request, or from the URL, are available through the params method
-
# which returns a hash. For example, an action that was performed through <tt>/posts?category=All&limit=5</tt> will include
-
# <tt>{ "category" => "All", "limit" => 5 }</tt> in params.
-
#
-
# It's also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as:
-
#
-
# <input type="text" name="post[name]" value="david">
-
# <input type="text" name="post[address]" value="hyacintvej">
-
#
-
# A request stemming from a form holding these inputs will include <tt>{ "post" => { "name" => "david", "address" => "hyacintvej" } }</tt>.
-
# If the address input had been named "post[address][street]", the params would have included
-
# <tt>{ "post" => { "address" => { "street" => "hyacintvej" } } }</tt>. There's no limit to the depth of the nesting.
-
#
-
# == Sessions
-
#
-
# Sessions allow you to store objects in between requests. This is useful for objects that are not yet ready to be persisted,
-
# such as a Signup object constructed in a multi-paged process, or objects that don't change much and are needed all the time, such
-
# as a User object for a system that requires login. The session should not be used, however, as a cache for objects where it's likely
-
# they could be changed unknowingly. It's usually too much work to keep it all synchronized -- something databases already excel at.
-
#
-
# You can place objects in the session by using the <tt>session</tt> method, which accesses a hash:
-
#
-
# session[:person] = Person.authenticate(user_name, password)
-
#
-
# And retrieved again through the same hash:
-
#
-
# Hello #{session[:person]}
-
#
-
# For removing objects from the session, you can either assign a single key to +nil+:
-
#
-
# # removes :person from session
-
# session[:person] = nil
-
#
-
# or you can remove the entire session with +reset_session+.
-
#
-
# Sessions are stored by default in a browser cookie that's cryptographically signed, but unencrypted.
-
# This prevents the user from tampering with the session but also allows him to see its contents.
-
#
-
# Do not put secret information in cookie-based sessions!
-
#
-
# Other options for session storage:
-
#
-
# * ActiveRecord::SessionStore - Sessions are stored in your database, which works better than PStore with multiple app servers and,
-
# unlike CookieStore, hides your session contents from the user. To use ActiveRecord::SessionStore, set
-
#
-
# MyApplication::Application.config.session_store :active_record_store
-
#
-
# in your <tt>config/initializers/session_store.rb</tt> and run <tt>script/rails g session_migration</tt>.
-
#
-
# == Responses
-
#
-
# Each action results in a response, which holds the headers and document to be sent to the user's browser. The actual response
-
# object is generated automatically through the use of renders and redirects and requires no user intervention.
-
#
-
# == Renders
-
#
-
# Action Controller sends content to the user by using one of five rendering methods. The most versatile and common is the rendering
-
# of a template. Included in the Action Pack is the Action View, which enables rendering of ERB templates. It's automatically configured.
-
# The controller passes objects to the view by assigning instance variables:
-
#
-
# def show
-
# @post = Post.find(params[:id])
-
# end
-
#
-
# Which are then automatically available to the view:
-
#
-
# Title: <%= @post.title %>
-
#
-
# You don't have to rely on the automated rendering. For example, actions that could result in the rendering of different templates
-
# will use the manual rendering methods:
-
#
-
# def search
-
# @results = Search.find(params[:query])
-
# case @results
-
# when 0 then render :action => "no_results"
-
# when 1 then render :action => "show"
-
# when 2..10 then render :action => "show_many"
-
# end
-
# end
-
#
-
# Read more about writing ERB and Builder templates in ActionView::Base.
-
#
-
# == Redirects
-
#
-
# Redirects are used to move from one action to another. For example, after a <tt>create</tt> action, which stores a blog entry to the
-
# database, we might like to show the user the new entry. Because we're following good DRY principles (Don't Repeat Yourself), we're
-
# going to reuse (and redirect to) a <tt>show</tt> action that we'll assume has already been created. The code might look like this:
-
#
-
# def create
-
# @entry = Entry.new(params[:entry])
-
# if @entry.save
-
# # The entry was saved correctly, redirect to show
-
# redirect_to :action => 'show', :id => @entry.id
-
# else
-
# # things didn't go so well, do something else
-
# end
-
# end
-
#
-
# In this case, after saving our new entry to the database, the user is redirected to the <tt>show</tt> method, which is then executed.
-
# Note that this is an external HTTP-level redirection which will cause the browser to make a second request (a GET to the show action),
-
# and not some internal re-routing which calls both "create" and then "show" within one request.
-
#
-
# Learn more about <tt>redirect_to</tt> and what options you have in ActionController::Redirecting.
-
#
-
# == Calling multiple redirects or renders
-
#
-
# An action may contain only a single render or a single redirect. Attempting to try to do either again will result in a DoubleRenderError:
-
#
-
# def do_something
-
# redirect_to :action => "elsewhere"
-
# render :action => "overthere" # raises DoubleRenderError
-
# end
-
#
-
# If you need to redirect on the condition of something, then be sure to add "and return" to halt execution.
-
#
-
# def do_something
-
# redirect_to(:action => "elsewhere") and return if monkeys.nil?
-
# render :action => "overthere" # won't be called if monkeys is nil
-
# end
-
#
-
1
class Base < Metal
-
1
abstract!
-
-
1
def self.without_modules(*modules)
-
modules = modules.map do |m|
-
m.is_a?(Symbol) ? ActionController.const_get(m) : m
-
end
-
-
MODULES - modules
-
end
-
-
1
MODULES = [
-
AbstractController::Layouts,
-
AbstractController::Translation,
-
AbstractController::AssetPaths,
-
-
Helpers,
-
HideActions,
-
UrlFor,
-
Redirecting,
-
Rendering,
-
Renderers::All,
-
ConditionalGet,
-
RackDelegation,
-
SessionManagement,
-
Caching,
-
MimeResponds,
-
ImplicitRender,
-
-
Cookies,
-
Flash,
-
RequestForgeryProtection,
-
ForceSSL,
-
Streaming,
-
DataStreaming,
-
RecordIdentifier,
-
HttpAuthentication::Basic::ControllerMethods,
-
HttpAuthentication::Digest::ControllerMethods,
-
HttpAuthentication::Token::ControllerMethods,
-
-
# Before callbacks should also be executed the earliest as possible, so
-
# also include them at the bottom.
-
AbstractController::Callbacks,
-
-
# Append rescue at the bottom to wrap as much as possible.
-
Rescue,
-
-
# Add instrumentations hooks at the bottom, to ensure they instrument
-
# all the methods properly.
-
Instrumentation,
-
-
# Params wrapper should come before instrumentation so they are
-
# properly showed in logs
-
ParamsWrapper
-
]
-
-
1
MODULES.each do |mod|
-
29
include mod
-
end
-
-
# Rails 2.x compatibility
-
1
include ActionController::Compatibility
-
-
1
ActiveSupport.run_load_hooks(:action_controller, self)
-
end
-
end
-
1
require 'fileutils'
-
1
require 'uri'
-
1
require 'set'
-
-
1
module ActionController #:nodoc:
-
# \Caching is a cheap way of speeding up slow applications by keeping the result of
-
# calculations, renderings, and database calls around for subsequent requests.
-
# Action Controller affords you three approaches in varying levels of granularity:
-
# Page, Action, Fragment.
-
#
-
# You can read more about each approach and the sweeping assistance by clicking the
-
# modules below.
-
#
-
# Note: To turn off all caching and sweeping, set
-
# config.action_controller.perform_caching = false.
-
#
-
# == \Caching stores
-
#
-
# All the caching stores from ActiveSupport::Cache are available to be used as backends
-
# for Action Controller caching. This setting only affects action and fragment caching
-
# as page caching is always written to disk.
-
#
-
# Configuration examples (MemoryStore is the default):
-
#
-
# config.action_controller.cache_store = :memory_store
-
# config.action_controller.cache_store = :file_store, "/path/to/cache/directory"
-
# config.action_controller.cache_store = :drb_store, "druby://localhost:9192"
-
# config.action_controller.cache_store = :mem_cache_store, "localhost"
-
# config.action_controller.cache_store = :mem_cache_store, Memcached::Rails.new("localhost:11211")
-
# config.action_controller.cache_store = MyOwnStore.new("parameter")
-
1
module Caching
-
1
extend ActiveSupport::Concern
-
1
extend ActiveSupport::Autoload
-
-
1
eager_autoload do
-
1
autoload :Actions
-
1
autoload :Fragments
-
1
autoload :Pages
-
1
autoload :Sweeper, 'action_controller/caching/sweeping'
-
1
autoload :Sweeping, 'action_controller/caching/sweeping'
-
end
-
-
1
module ConfigMethods
-
1
def cache_store
-
1
config.cache_store
-
end
-
-
1
def cache_store=(store)
-
1
config.cache_store = ActiveSupport::Cache.lookup_store(store)
-
end
-
-
1
private
-
-
1
def cache_configured?
-
perform_caching && cache_store
-
end
-
end
-
-
1
include ConfigMethods
-
1
include Pages, Actions, Fragments
-
1
include Sweeping if defined?(ActiveRecord)
-
-
1
included do
-
1
extend ConfigMethods
-
-
1
config_accessor :perform_caching
-
1
self.perform_caching = true if perform_caching.nil?
-
end
-
-
1
def caching_allowed?
-
request.get? && response.status == 200
-
end
-
-
1
protected
-
# Convenience accessor
-
1
def cache(key, options = {}, &block)
-
if cache_configured?
-
cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
-
else
-
yield
-
end
-
end
-
end
-
end
-
1
require 'set'
-
-
1
module ActionController #:nodoc:
-
1
module Caching
-
# Action caching is similar to page caching by the fact that the entire
-
# output of the response is cached, but unlike page caching, every
-
# request still goes through Action Pack. The key benefit of this is
-
# that filters run before the cache is served, which allows for
-
# authentication and other restrictions on whether someone is allowed
-
# to execute such action. Example:
-
#
-
# class ListsController < ApplicationController
-
# before_filter :authenticate, :except => :public
-
#
-
# caches_page :public
-
# caches_action :index, :show
-
# end
-
#
-
# In this example, the +public+ action doesn't require authentication
-
# so it's possible to use the faster page caching. On the other hand
-
# +index+ and +show+ require authentication. They can still be cached,
-
# but we need action caching for them.
-
#
-
# Action caching uses fragment caching internally and an around
-
# filter to do the job. The fragment cache is named according to
-
# the host and path of the request. A page that is accessed at
-
# <tt>http://david.example.com/lists/show/1</tt> will result in a fragment named
-
# <tt>david.example.com/lists/show/1</tt>. This allows the cacher to
-
# differentiate between <tt>david.example.com/lists/</tt> and
-
# <tt>jamis.example.com/lists/</tt> -- which is a helpful way of assisting
-
# the subdomain-as-account-key pattern.
-
#
-
# Different representations of the same resource, e.g.
-
# <tt>http://david.example.com/lists</tt> and
-
# <tt>http://david.example.com/lists.xml</tt>
-
# are treated like separate requests and so are cached separately.
-
# Keep in mind when expiring an action cache that
-
# <tt>:action => 'lists'</tt> is not the same as
-
# <tt>:action => 'list', :format => :xml</tt>.
-
#
-
# You can set modify the default action cache path by passing a
-
# <tt>:cache_path</tt> option. This will be passed directly to
-
# <tt>ActionCachePath.path_for</tt>. This is handy for actions with
-
# multiple possible routes that should be cached differently. If a
-
# block is given, it is called with the current controller instance.
-
#
-
# And you can also use <tt>:if</tt> (or <tt>:unless</tt>) to pass a
-
# proc that specifies when the action should be cached.
-
#
-
# Finally, if you are using memcached, you can also pass <tt>:expires_in</tt>.
-
#
-
# The following example depicts some of the points made above:
-
#
-
# class ListsController < ApplicationController
-
# before_filter :authenticate, :except => :public
-
#
-
# caches_page :public
-
#
-
# caches_action :index, :if => proc do
-
# !request.format.json? # cache if is not a JSON request
-
# end
-
#
-
# caches_action :show, :cache_path => { :project => 1 },
-
# :expires_in => 1.hour
-
#
-
# caches_action :feed, :cache_path => proc do
-
# if params[:user_id]
-
# user_list_url(params[:user_id, params[:id])
-
# else
-
# list_url(params[:id])
-
# end
-
# end
-
# end
-
#
-
# If you pass <tt>:layout => false</tt>, it will only cache your action
-
# content. That's useful when your layout has dynamic information.
-
#
-
# Warning: If the format of the request is determined by the Accept HTTP
-
# header the Content-Type of the cached response could be wrong because
-
# no information about the MIME type is stored in the cache key. So, if
-
# you first ask for MIME type M in the Accept header, a cache entry is
-
# created, and then perform a second request to the same resource asking
-
# for a different MIME type, you'd get the content cached for M.
-
#
-
# The <tt>:format</tt> parameter is taken into account though. The safest
-
# way to cache by MIME type is to pass the format in the route.
-
1
module Actions
-
1
extend ActiveSupport::Concern
-
-
1
module ClassMethods
-
# Declares that +actions+ should be cached.
-
# See ActionController::Caching::Actions for details.
-
1
def caches_action(*actions)
-
return unless cache_configured?
-
options = actions.extract_options!
-
options[:layout] = true unless options.key?(:layout)
-
filter_options = options.extract!(:if, :unless).merge(:only => actions)
-
cache_options = options.extract!(:layout, :cache_path).merge(:store_options => options)
-
-
around_filter ActionCacheFilter.new(cache_options), filter_options
-
end
-
end
-
-
1
def _save_fragment(name, options)
-
content = response_body
-
content = content.join if content.is_a?(Array)
-
-
if caching_allowed?
-
write_fragment(name, content, options)
-
else
-
content
-
end
-
end
-
-
1
protected
-
1
def expire_action(options = {})
-
return unless cache_configured?
-
-
actions = options[:action]
-
if actions.is_a?(Array)
-
actions.each {|action| expire_action(options.merge(:action => action)) }
-
else
-
expire_fragment(ActionCachePath.new(self, options, false).path)
-
end
-
end
-
-
1
class ActionCacheFilter #:nodoc:
-
1
def initialize(options, &block)
-
@cache_path, @store_options, @cache_layout =
-
options.values_at(:cache_path, :store_options, :layout)
-
end
-
-
1
def filter(controller)
-
path_options = if @cache_path.respond_to?(:call)
-
controller.instance_exec(controller, &@cache_path)
-
else
-
@cache_path
-
end
-
-
cache_path = ActionCachePath.new(controller, path_options || {})
-
-
body = controller.read_fragment(cache_path.path, @store_options)
-
-
unless body
-
controller.action_has_layout = false unless @cache_layout
-
yield
-
controller.action_has_layout = true
-
body = controller._save_fragment(cache_path.path, @store_options)
-
end
-
-
body = controller.render_to_string(:text => body, :layout => true) unless @cache_layout
-
-
controller.response_body = body
-
controller.content_type = Mime[cache_path.extension || :html]
-
end
-
end
-
-
1
class ActionCachePath
-
1
attr_reader :path, :extension
-
-
# If +infer_extension+ is true, the cache path extension is looked up from the request's
-
# path and format. This is desirable when reading and writing the cache, but not when
-
# expiring the cache - expire_action should expire the same files regardless of the
-
# request format.
-
1
def initialize(controller, options = {}, infer_extension = true)
-
if infer_extension
-
@extension = controller.params[:format]
-
options.reverse_merge!(:format => @extension) if options.is_a?(Hash)
-
end
-
-
path = controller.url_for(options).split(%r{://}).last
-
@path = normalize!(path)
-
end
-
-
1
private
-
1
def normalize!(path)
-
path << 'index' if path[-1] == ?/
-
path << ".#{extension}" if extension and !path.ends_with?(extension)
-
URI.parser.unescape(path)
-
end
-
end
-
end
-
end
-
end
-
1
module ActionController #:nodoc:
-
1
module Caching
-
# Fragment caching is used for caching various blocks within
-
# views without caching the entire action as a whole. This is
-
# useful when certain elements of an action change frequently or
-
# depend on complicated state while other parts rarely change or
-
# can be shared amongst multiple parties. The caching is done using
-
# the <tt>cache</tt> helper available in the Action View. A
-
# template with fragment caching might look like:
-
#
-
# <b>Hello <%= @name %></b>
-
#
-
# <% cache do %>
-
# All the topics in the system:
-
# <%= render :partial => "topic", :collection => Topic.find(:all) %>
-
# <% end %>
-
#
-
# This cache will bind the name of the action that called it, so if
-
# this code was part of the view for the topics/list action, you
-
# would be able to invalidate it using:
-
#
-
# expire_fragment(:controller => "topics", :action => "list")
-
#
-
# This default behavior is limited if you need to cache multiple
-
# fragments per action or if the action itself is cached using
-
# <tt>caches_action</tt>. To remedy this, there is an option to
-
# qualify the name of the cached fragment by using the
-
# <tt>:action_suffix</tt> option:
-
#
-
# <% cache(:action => "list", :action_suffix => "all_topics") do %>
-
#
-
# That would result in a name such as
-
# <tt>/topics/list/all_topics</tt>, avoiding conflicts with the
-
# action cache and with any fragments that use a different suffix.
-
# Note that the URL doesn't have to really exist or be callable
-
# - the url_for system is just used to generate unique cache names
-
# that we can refer to when we need to expire the cache.
-
#
-
# The expiration call for this example is:
-
#
-
# expire_fragment(:controller => "topics",
-
# :action => "list",
-
# :action_suffix => "all_topics")
-
1
module Fragments
-
# Given a key (as described in <tt>expire_fragment</tt>), returns
-
# a key suitable for use in reading, writing, or expiring a
-
# cached fragment. If the key is a hash, the generated key is the
-
# return value of url_for on that hash (without the protocol).
-
# All keys are prefixed with <tt>views/</tt> and uses
-
# ActiveSupport::Cache.expand_cache_key for the expansion.
-
1
def fragment_cache_key(key)
-
ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
-
end
-
-
# Writes <tt>content</tt> to the location signified by
-
# <tt>key</tt> (see <tt>expire_fragment</tt> for acceptable formats).
-
1
def write_fragment(key, content, options = nil)
-
return content unless cache_configured?
-
-
key = fragment_cache_key(key)
-
instrument_fragment_cache :write_fragment, key do
-
content = content.to_str
-
cache_store.write(key, content, options)
-
end
-
content
-
end
-
-
# Reads a cached fragment from the location signified by <tt>key</tt>
-
# (see <tt>expire_fragment</tt> for acceptable formats).
-
1
def read_fragment(key, options = nil)
-
return unless cache_configured?
-
-
key = fragment_cache_key(key)
-
instrument_fragment_cache :read_fragment, key do
-
result = cache_store.read(key, options)
-
result.respond_to?(:html_safe) ? result.html_safe : result
-
end
-
end
-
-
# Check if a cached fragment from the location signified by
-
# <tt>key</tt> exists (see <tt>expire_fragment</tt> for acceptable formats)
-
1
def fragment_exist?(key, options = nil)
-
return unless cache_configured?
-
key = fragment_cache_key(key)
-
-
instrument_fragment_cache :exist_fragment?, key do
-
cache_store.exist?(key, options)
-
end
-
end
-
-
# Removes fragments from the cache.
-
#
-
# +key+ can take one of three forms:
-
#
-
# * String - This would normally take the form of a path, like
-
# <tt>pages/45/notes</tt>.
-
# * Hash - Treated as an implicit call to +url_for+, like
-
# <tt>{:controller => "pages", :action => "notes", :id => 45}</tt>
-
# * Regexp - Will remove any fragment that matches, so
-
# <tt>%r{pages/\d*/notes}</tt> might remove all notes. Make sure you
-
# don't use anchors in the regex (<tt>^</tt> or <tt>$</tt>) because
-
# the actual filename matched looks like
-
# <tt>./cache/filename/path.cache</tt>. Note: Regexp expiration is
-
# only supported on caches that can iterate over all keys (unlike
-
# memcached).
-
#
-
# +options+ is passed through to the cache store's <tt>delete</tt>
-
# method (or <tt>delete_matched</tt>, for Regexp keys.)
-
1
def expire_fragment(key, options = nil)
-
return unless cache_configured?
-
key = fragment_cache_key(key) unless key.is_a?(Regexp)
-
message = nil
-
-
instrument_fragment_cache :expire_fragment, key do
-
if key.is_a?(Regexp)
-
cache_store.delete_matched(key, options)
-
else
-
cache_store.delete(key, options)
-
end
-
end
-
end
-
-
1
def instrument_fragment_cache(name, key)
-
ActiveSupport::Notifications.instrument("#{name}.action_controller", :key => key){ yield }
-
end
-
end
-
end
-
end
-
1
require 'fileutils'
-
1
require 'active_support/core_ext/class/attribute_accessors'
-
-
1
module ActionController #:nodoc:
-
1
module Caching
-
# Page caching is an approach to caching where the entire action output of is stored as a HTML file that the web server
-
# can serve without going through Action Pack. This is the fastest way to cache your content as opposed to going dynamically
-
# through the process of generating the content. Unfortunately, this incredible speed-up is only available to stateless pages
-
# where all visitors are treated the same. Content management systems -- including weblogs and wikis -- have many pages that are
-
# a great fit for this approach, but account-based systems where people log in and manipulate their own data are often less
-
# likely candidates.
-
#
-
# Specifying which actions to cache is done through the <tt>caches_page</tt> class method:
-
#
-
# class WeblogController < ActionController::Base
-
# caches_page :show, :new
-
# end
-
#
-
# This will generate cache files such as <tt>weblog/show/5.html</tt> and <tt>weblog/new.html</tt>,
-
# which match the URLs used to trigger the dynamic generation. This is how the web server is able
-
# pick up a cache file when it exists and otherwise let the request pass on to Action Pack to generate it.
-
#
-
# Expiration of the cache is handled by deleting the cached file, which results in a lazy regeneration approach where the cache
-
# is not restored before another hit is made against it. The API for doing so mimics the options from +url_for+ and friends:
-
#
-
# class WeblogController < ActionController::Base
-
# def update
-
# List.update(params[:list][:id], params[:list])
-
# expire_page :action => "show", :id => params[:list][:id]
-
# redirect_to :action => "show", :id => params[:list][:id]
-
# end
-
# end
-
#
-
# Additionally, you can expire caches using Sweepers that act on changes in the model to determine when a cache is supposed to be
-
# expired.
-
1
module Pages
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
##
-
# :singleton-method:
-
# The cache directory should be the document root for the web server and is set using <tt>Base.page_cache_directory = "/document/root"</tt>.
-
# For Rails, this directory has already been set to Rails.public_path (which is usually set to <tt>Rails.root + "/public"</tt>). Changing
-
# this setting can be useful to avoid naming conflicts with files in <tt>public/</tt>, but doing so will likely require configuring your
-
# web server to look in the new location for cached files.
-
1
config_accessor :page_cache_directory
-
1
self.page_cache_directory ||= ''
-
-
##
-
# :singleton-method:
-
# Most Rails requests do not have an extension, such as <tt>/weblog/new</tt>. In these cases, the page caching mechanism will add one in
-
# order to make it easy for the cached files to be picked up properly by the web server. By default, this cache extension is <tt>.html</tt>.
-
# If you want something else, like <tt>.php</tt> or <tt>.shtml</tt>, just set Base.page_cache_extension. In cases where a request already has an
-
# extension, such as <tt>.xml</tt> or <tt>.rss</tt>, page caching will not add an extension. This allows it to work well with RESTful apps.
-
1
config_accessor :page_cache_extension
-
1
self.page_cache_extension ||= '.html'
-
end
-
-
1
module ClassMethods
-
# Expires the page that was cached with the +path+ as a key. Example:
-
# expire_page "/lists/show"
-
1
def expire_page(path)
-
return unless perform_caching
-
path = page_cache_path(path)
-
-
instrument_page_cache :expire_page, path do
-
File.delete(path) if File.exist?(path)
-
end
-
end
-
-
# Manually cache the +content+ in the key determined by +path+. Example:
-
# cache_page "I'm the cached content", "/lists/show"
-
1
def cache_page(content, path, extension = nil)
-
return unless perform_caching
-
path = page_cache_path(path, extension)
-
-
instrument_page_cache :write_page, path do
-
FileUtils.makedirs(File.dirname(path))
-
File.open(path, "wb+") { |f| f.write(content) }
-
end
-
end
-
-
# Caches the +actions+ using the page-caching approach that'll store the cache in a path within the page_cache_directory that
-
# matches the triggering url.
-
#
-
# Usage:
-
#
-
# # cache the index action
-
# caches_page :index
-
#
-
# # cache the index action except for JSON requests
-
# caches_page :index, :if => Proc.new { |c| !c.request.format.json? }
-
1
def caches_page(*actions)
-
return unless perform_caching
-
options = actions.extract_options!
-
after_filter({:only => actions}.merge(options)) { |c| c.cache_page }
-
end
-
-
1
private
-
1
def page_cache_file(path, extension)
-
name = (path.empty? || path == "/") ? "/index" : URI.parser.unescape(path.chomp('/'))
-
unless (name.split('/').last || name).include? '.'
-
name << (extension || self.page_cache_extension)
-
end
-
return name
-
end
-
-
1
def page_cache_path(path, extension = nil)
-
page_cache_directory.to_s + page_cache_file(path, extension)
-
end
-
-
1
def instrument_page_cache(name, path)
-
ActiveSupport::Notifications.instrument("#{name}.action_controller", :path => path){ yield }
-
end
-
end
-
-
# Expires the page that was cached with the +options+ as a key. Example:
-
# expire_page :controller => "lists", :action => "show"
-
1
def expire_page(options = {})
-
return unless self.class.perform_caching
-
-
if options.is_a?(Hash)
-
if options[:action].is_a?(Array)
-
options[:action].dup.each do |action|
-
self.class.expire_page(url_for(options.merge(:only_path => true, :action => action)))
-
end
-
else
-
self.class.expire_page(url_for(options.merge(:only_path => true)))
-
end
-
else
-
self.class.expire_page(options)
-
end
-
end
-
-
# Manually cache the +content+ in the key determined by +options+. If no content is provided, the contents of response.body is used
-
# If no options are provided, the requested url is used. Example:
-
# cache_page "I'm the cached content", :controller => "lists", :action => "show"
-
1
def cache_page(content = nil, options = nil)
-
return unless self.class.perform_caching && caching_allowed?
-
-
path = case options
-
when Hash
-
url_for(options.merge(:only_path => true, :format => params[:format]))
-
when String
-
options
-
else
-
request.path
-
end
-
-
if (type = Mime::LOOKUP[self.content_type]) && (type_symbol = type.symbol).present?
-
extension = ".#{type_symbol}"
-
end
-
-
self.class.cache_page(content || response.body, path, extension)
-
end
-
-
end
-
end
-
end
-
1
module ActionController #:nodoc:
-
1
module Caching
-
# Sweepers are the terminators of the caching world and responsible for expiring caches when model objects change.
-
# They do this by being half-observers, half-filters and implementing callbacks for both roles. A Sweeper example:
-
#
-
# class ListSweeper < ActionController::Caching::Sweeper
-
# observe List, Item
-
#
-
# def after_save(record)
-
# list = record.is_a?(List) ? record : record.list
-
# expire_page(:controller => "lists", :action => %w( show public feed ), :id => list.id)
-
# expire_action(:controller => "lists", :action => "all")
-
# list.shares.each { |share| expire_page(:controller => "lists", :action => "show", :id => share.url_key) }
-
# end
-
# end
-
#
-
# The sweeper is assigned in the controllers that wish to have its job performed using the <tt>cache_sweeper</tt> class method:
-
#
-
# class ListsController < ApplicationController
-
# caches_action :index, :show, :public, :feed
-
# cache_sweeper :list_sweeper, :only => [ :edit, :destroy, :share ]
-
# end
-
#
-
# In the example above, four actions are cached and three actions are responsible for expiring those caches.
-
#
-
# You can also name an explicit class in the declaration of a sweeper, which is needed if the sweeper is in a module:
-
#
-
# class ListsController < ApplicationController
-
# caches_action :index, :show, :public, :feed
-
# cache_sweeper OpenBar::Sweeper, :only => [ :edit, :destroy, :share ]
-
# end
-
1
module Sweeping
-
1
extend ActiveSupport::Concern
-
-
1
module ClassMethods #:nodoc:
-
1
def cache_sweeper(*sweepers)
-
configuration = sweepers.extract_options!
-
-
sweepers.each do |sweeper|
-
ActiveRecord::Base.observers << sweeper if defined?(ActiveRecord) and defined?(ActiveRecord::Base)
-
sweeper_instance = (sweeper.is_a?(Symbol) ? Object.const_get(sweeper.to_s.classify) : sweeper).instance
-
-
if sweeper_instance.is_a?(Sweeper)
-
around_filter(sweeper_instance, :only => configuration[:only])
-
else
-
after_filter(sweeper_instance, :only => configuration[:only])
-
end
-
end
-
end
-
end
-
end
-
-
1
if defined?(ActiveRecord) and defined?(ActiveRecord::Observer)
-
1
class Sweeper < ActiveRecord::Observer #:nodoc:
-
1
attr_accessor :controller
-
-
1
def before(controller)
-
self.controller = controller
-
callback(:before) if controller.perform_caching
-
true # before method from sweeper should always return true
-
end
-
-
1
def after(controller)
-
self.controller = controller
-
callback(:after) if controller.perform_caching
-
# Clean up, so that the controller can be collected after this request
-
self.controller = nil
-
end
-
-
1
protected
-
# gets the action cache path for the given options.
-
1
def action_path_for(options)
-
Actions::ActionCachePath.new(controller, options).path
-
end
-
-
# Retrieve instance variables set in the controller.
-
1
def assigns(key)
-
controller.instance_variable_get("@#{key}")
-
end
-
-
1
private
-
1
def callback(timing)
-
controller_callback_method_name = "#{timing}_#{controller.controller_name.underscore}"
-
action_callback_method_name = "#{controller_callback_method_name}_#{controller.action_name}"
-
-
__send__(controller_callback_method_name) if respond_to?(controller_callback_method_name, true)
-
__send__(action_callback_method_name) if respond_to?(action_callback_method_name, true)
-
end
-
-
1
def method_missing(method, *arguments, &block)
-
return if @controller.nil?
-
@controller.__send__(method, *arguments, &block)
-
end
-
end
-
end
-
end
-
end
-
1
ActionController::Integration = ActionDispatch::Integration
-
1
ActionController::IntegrationTest = ActionDispatch::IntegrationTest
-
1
require 'active_support/core_ext/object/blank'
-
-
1
module ActionController
-
1
class LogSubscriber < ActiveSupport::LogSubscriber
-
1
INTERNAL_PARAMS = %w(controller action format _method only_path)
-
-
1
def start_processing(event)
-
payload = event.payload
-
params = payload[:params].except(*INTERNAL_PARAMS)
-
format = payload[:format]
-
format = format.to_s.upcase if format.is_a?(Symbol)
-
-
info " Processing by #{payload[:controller]}##{payload[:action]} as #{format}"
-
info " Parameters: #{params.inspect}" unless params.empty?
-
end
-
-
1
def process_action(event)
-
payload = event.payload
-
additions = ActionController::Base.log_process_action(payload)
-
-
status = payload[:status]
-
if status.nil? && payload[:exception].present?
-
status = Rack::Utils.status_code(ActionDispatch::ShowExceptions.rescue_responses[payload[:exception].first]) rescue nil
-
end
-
message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in %.0fms" % event.duration
-
message << " (#{additions.join(" | ")})" unless additions.blank?
-
-
info(message)
-
end
-
-
1
def send_file(event)
-
message = "Sent file %s"
-
message << " (%.1fms)"
-
info(message % [event.payload[:path], event.duration])
-
end
-
-
1
def redirect_to(event)
-
info "Redirected to #{event.payload[:location]}"
-
end
-
-
1
def send_data(event)
-
info("Sent data %s (%.1fms)" % [event.payload[:filename], event.duration])
-
end
-
-
1
%w(write_fragment read_fragment exist_fragment?
-
expire_fragment expire_page write_page).each do |method|
-
6
class_eval <<-METHOD, __FILE__, __LINE__ + 1
-
def #{method}(event)
-
key_or_path = event.payload[:key] || event.payload[:path]
-
human_name = #{method.to_s.humanize.inspect}
-
info("\#{human_name} \#{key_or_path} \#{"(%.1fms)" % event.duration}")
-
end
-
METHOD
-
end
-
-
1
def logger
-
ActionController::Base.logger
-
end
-
end
-
end
-
-
1
ActionController::LogSubscriber.attach_to :action_controller
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'action_dispatch/middleware/stack'
-
-
1
module ActionController
-
# Extend ActionDispatch middleware stack to make it aware of options
-
# allowing the following syntax in controllers:
-
#
-
# class PostsController < ApplicationController
-
# use AuthenticationMiddleware, :except => [:index, :show]
-
# end
-
#
-
1
class MiddlewareStack < ActionDispatch::MiddlewareStack #:nodoc:
-
1
class Middleware < ActionDispatch::MiddlewareStack::Middleware #:nodoc:
-
1
def initialize(klass, *args, &block)
-
options = args.extract_options!
-
@only = Array(options.delete(:only)).map(&:to_s)
-
@except = Array(options.delete(:except)).map(&:to_s)
-
args << options unless options.empty?
-
super
-
end
-
-
1
def valid?(action)
-
if @only.present?
-
@only.include?(action)
-
elsif @except.present?
-
!@except.include?(action)
-
else
-
true
-
end
-
end
-
end
-
-
1
def build(action, app=nil, &block)
-
app ||= block
-
action = action.to_s
-
raise "MiddlewareStack#build requires an app" unless app
-
-
middlewares.reverse.inject(app) do |a, middleware|
-
middleware.valid?(action) ?
-
middleware.build(a) : a
-
end
-
end
-
end
-
-
# <tt>ActionController::Metal</tt> is the simplest possible controller, providing a
-
# valid Rack interface without the additional niceties provided by
-
# <tt>ActionController::Base</tt>.
-
#
-
# A sample metal controller might look like this:
-
#
-
# class HelloController < ActionController::Metal
-
# def index
-
# self.response_body = "Hello World!"
-
# end
-
# end
-
#
-
# And then to route requests to your metal controller, you would add
-
# something like this to <tt>config/routes.rb</tt>:
-
#
-
# match 'hello', :to => HelloController.action(:index)
-
#
-
# The +action+ method returns a valid Rack application for the \Rails
-
# router to dispatch to.
-
#
-
# == Rendering Helpers
-
#
-
# <tt>ActionController::Metal</tt> by default provides no utilities for rendering
-
# views, partials, or other responses aside from explicitly calling of
-
# <tt>response_body=</tt>, <tt>content_type=</tt>, and <tt>status=</tt>. To
-
# add the render helpers you're used to having in a normal controller, you
-
# can do the following:
-
#
-
# class HelloController < ActionController::Metal
-
# include ActionController::Rendering
-
# append_view_path "#{Rails.root}/app/views"
-
#
-
# def index
-
# render "hello/index"
-
# end
-
# end
-
#
-
# == Redirection Helpers
-
#
-
# To add redirection helpers to your metal controller, do the following:
-
#
-
# class HelloController < ActionController::Metal
-
# include ActionController::Redirecting
-
# include Rails.application.routes.url_helpers
-
#
-
# def index
-
# redirect_to root_url
-
# end
-
# end
-
#
-
# == Other Helpers
-
#
-
# You can refer to the modules included in <tt>ActionController::Base</tt> to see
-
# other features you can bring into your metal controller.
-
#
-
1
class Metal < AbstractController::Base
-
1
abstract!
-
-
1
attr_internal_writer :env
-
-
1
def env
-
@_env ||= {}
-
end
-
-
# Returns the last part of the controller's name, underscored, without the ending
-
# <tt>Controller</tt>. For instance, PostsController returns <tt>posts</tt>.
-
# Namespaces are left out, so Admin::PostsController returns <tt>posts</tt> as well.
-
#
-
# ==== Returns
-
# * <tt>string</tt>
-
1
def self.controller_name
-
1
@controller_name ||= self.name.demodulize.sub(/Controller$/, '').underscore
-
end
-
-
# Delegates to the class' <tt>controller_name</tt>
-
1
def controller_name
-
self.class.controller_name
-
end
-
-
# The details below can be overridden to support a specific
-
# Request and Response object. The default ActionController::Base
-
# implementation includes RackDelegation, which makes a request
-
# and response object available. You might wish to control the
-
# environment and response manually for performance reasons.
-
-
1
attr_internal :headers, :response, :request
-
1
delegate :session, :to => "@_request"
-
-
1
def initialize
-
@_headers = {"Content-Type" => "text/html"}
-
@_status = 200
-
@_request = nil
-
@_response = nil
-
@_routes = nil
-
super
-
end
-
-
1
def params
-
@_params ||= request.parameters
-
end
-
-
1
def params=(val)
-
@_params = val
-
end
-
-
# Basic implementations for content_type=, location=, and headers are
-
# provided to reduce the dependency on the RackDelegation module
-
# in Renderer and Redirector.
-
-
1
def content_type=(type)
-
headers["Content-Type"] = type.to_s
-
end
-
-
1
def content_type
-
headers["Content-Type"]
-
end
-
-
1
def location
-
headers["Location"]
-
end
-
-
1
def location=(url)
-
headers["Location"] = url
-
end
-
-
# basic url_for that can be overridden for more robust functionality
-
1
def url_for(string)
-
string
-
end
-
-
1
def status
-
@_status
-
end
-
-
1
def status=(status)
-
@_status = Rack::Utils.status_code(status)
-
end
-
-
1
def response_body=(val)
-
body = val.nil? ? nil : (val.respond_to?(:each) ? val : [val])
-
super body
-
end
-
-
1
def dispatch(name, request) #:nodoc:
-
@_request = request
-
@_env = request.env
-
@_env['action_controller.instance'] = self
-
process(name)
-
to_a
-
end
-
-
1
def to_a #:nodoc:
-
response ? response.to_a : [status, headers, response_body]
-
end
-
-
1
class_attribute :middleware_stack
-
1
self.middleware_stack = ActionController::MiddlewareStack.new
-
-
1
def self.inherited(base) #nodoc:
-
5
base.middleware_stack = self.middleware_stack.dup
-
5
super
-
end
-
-
# Adds given middleware class and its args to bottom of middleware_stack
-
1
def self.use(*args, &block)
-
middleware_stack.use(*args, &block)
-
end
-
-
# Alias for middleware_stack
-
1
def self.middleware
-
middleware_stack
-
end
-
-
# Makes the controller a rack endpoint that points to the action in
-
# the given env's action_dispatch.request.path_parameters key.
-
1
def self.call(env)
-
action(env['action_dispatch.request.path_parameters'][:action]).call(env)
-
end
-
-
# Return a rack endpoint for the given action. Memoize the endpoint, so
-
# multiple calls into MyController.action will return the same object
-
# for the same action.
-
#
-
# ==== Parameters
-
# * <tt>action</tt> - An action name
-
#
-
# ==== Returns
-
# * <tt>proc</tt> - A rack application
-
1
def self.action(name, klass = ActionDispatch::Request)
-
middleware_stack.build(name.to_s) do |env|
-
new.dispatch(name, klass.new(env))
-
end
-
end
-
end
-
end
-
1
module ActionController
-
1
module Compatibility
-
1
extend ActiveSupport::Concern
-
-
1
class ::ActionController::ActionControllerError < StandardError #:nodoc:
-
end
-
-
# Temporary hax
-
1
included do
-
1
::ActionController::UnknownAction = ::AbstractController::ActionNotFound
-
1
::ActionController::DoubleRenderError = ::AbstractController::DoubleRenderError
-
-
# ROUTES TODO: This should be handled by a middleware and route generation
-
# should be able to handle SCRIPT_NAME
-
1
self.config.relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT']
-
-
1
class << self
-
1
delegate :default_charset=, :to => "ActionDispatch::Response"
-
end
-
-
1
self.protected_instance_variables = %w(
-
@_status @_headers @_params @_env @_response @_request
-
@_view_runtime @_stream @_url_options @_action_has_layout
-
)
-
-
1
def rescue_action(env)
-
raise env["action_dispatch.rescue.exception"]
-
end
-
end
-
-
# For old tests
-
1
def initialize_template_class(*) end
-
1
def assign_shortcuts(*) end
-
-
1
def _normalize_options(options)
-
options[:text] = nil if options.delete(:nothing) == true
-
options[:text] = " " if options.key?(:text) && options[:text].nil?
-
super
-
end
-
-
1
def render_to_body(options)
-
options[:template].sub!(/^\//, '') if options.key?(:template)
-
super || " "
-
end
-
-
1
def _handle_method_missing
-
method_missing(@_action_name.to_sym)
-
end
-
-
1
def method_for_action(action_name)
-
super || (respond_to?(:method_missing) && "_handle_method_missing")
-
end
-
-
1
def performed?
-
response_body
-
end
-
end
-
end
-
1
module ActionController
-
1
module ConditionalGet
-
1
extend ActiveSupport::Concern
-
-
1
include RackDelegation
-
1
include Head
-
-
# Sets the etag, last_modified, or both on the response and renders a
-
# <tt>304 Not Modified</tt> response if the request is already fresh.
-
#
-
# Parameters:
-
# * <tt>:etag</tt>
-
# * <tt>:last_modified</tt>
-
# * <tt>:public</tt> By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches).
-
#
-
# Example:
-
#
-
# def show
-
# @article = Article.find(params[:id])
-
# fresh_when(:etag => @article, :last_modified => @article.created_at, :public => true)
-
# end
-
#
-
# This will render the show template if the request isn't sending a matching etag or
-
# If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match.
-
#
-
1
def fresh_when(options)
-
options.assert_valid_keys(:etag, :last_modified, :public)
-
-
response.etag = options[:etag] if options[:etag]
-
response.last_modified = options[:last_modified] if options[:last_modified]
-
response.cache_control[:public] = true if options[:public]
-
-
head :not_modified if request.fresh?(response)
-
end
-
-
# Sets the etag and/or last_modified on the response and checks it against
-
# the client request. If the request doesn't match the options provided, the
-
# request is considered stale and should be generated from scratch. Otherwise,
-
# it's fresh and we don't need to generate anything and a reply of <tt>304 Not Modified</tt> is sent.
-
#
-
# Parameters:
-
# * <tt>:etag</tt>
-
# * <tt>:last_modified</tt>
-
# * <tt>:public</tt> By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches).
-
#
-
# Example:
-
#
-
# def show
-
# @article = Article.find(params[:id])
-
#
-
# if stale?(:etag => @article, :last_modified => @article.created_at)
-
# @statistics = @article.really_expensive_call
-
# respond_to do |format|
-
# # all the supported formats
-
# end
-
# end
-
# end
-
1
def stale?(options)
-
fresh_when(options)
-
!request.fresh?(response)
-
end
-
-
# Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a <tt>private</tt> instruction, so that
-
# intermediate caches must not cache the response.
-
#
-
# Examples:
-
# expires_in 20.minutes
-
# expires_in 3.hours, :public => true
-
# expires_in 3.hours, 'max-stale' => 5.hours, :public => true
-
#
-
# This method will overwrite an existing Cache-Control header.
-
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
-
1
def expires_in(seconds, options = {}) #:doc:
-
response.cache_control.merge!(:max_age => seconds, :public => options.delete(:public))
-
options.delete(:private)
-
-
response.cache_control[:extras] = options.map {|k,v| "#{k}=#{v}"}
-
end
-
-
# Sets a HTTP 1.1 Cache-Control header of <tt>no-cache</tt> so no caching should occur by the browser or
-
# intermediate caches (like caching proxy servers).
-
1
def expires_now #:doc:
-
response.cache_control.replace(:no_cache => true)
-
end
-
end
-
end
-
1
module ActionController #:nodoc:
-
1
module Cookies
-
1
extend ActiveSupport::Concern
-
-
1
include RackDelegation
-
-
1
included do
-
1
helper_method :cookies
-
end
-
-
1
private
-
1
def cookies
-
request.cookie_jar
-
end
-
end
-
end
-
1
require 'active_support/core_ext/file/path'
-
-
1
module ActionController #:nodoc:
-
# Methods for sending arbitrary data and for streaming files to the browser,
-
# instead of rendering.
-
1
module DataStreaming
-
1
extend ActiveSupport::Concern
-
-
1
include ActionController::Rendering
-
-
1
DEFAULT_SEND_FILE_OPTIONS = {
-
:type => 'application/octet-stream'.freeze,
-
:disposition => 'attachment'.freeze,
-
}.freeze
-
-
1
protected
-
# Sends the file. This uses a server-appropriate method (such as X-Sendfile)
-
# via the Rack::Sendfile middleware. The header to use is set via
-
# config.action_dispatch.x_sendfile_header.
-
# Your server can also configure this for you by setting the X-Sendfile-Type header.
-
#
-
# Be careful to sanitize the path parameter if it is coming from a web
-
# page. <tt>send_file(params[:path])</tt> allows a malicious user to
-
# download any file on your server.
-
#
-
# Options:
-
# * <tt>:filename</tt> - suggests a filename for the browser to use.
-
# Defaults to <tt>File.basename(path)</tt>.
-
# * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify
-
# either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json
-
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
-
# Valid values are 'inline' and 'attachment' (default).
-
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
-
# * <tt>:url_based_filename</tt> - set to +true+ if you want the browser guess the filename from
-
# the URL, which is necessary for i18n filenames on certain browsers
-
# (setting <tt>:filename</tt> overrides this option).
-
#
-
# The default Content-Type and Content-Disposition headers are
-
# set to download arbitrary binary files in as many browsers as
-
# possible. IE versions 4, 5, 5.5, and 6 are all known to have
-
# a variety of quirks (especially when downloading over SSL).
-
#
-
# Simple download:
-
#
-
# send_file '/path/to.zip'
-
#
-
# Show a JPEG in the browser:
-
#
-
# send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline'
-
#
-
# Show a 404 page in the browser:
-
#
-
# send_file '/path/to/404.html', :type => 'text/html; charset=utf-8', :status => 404
-
#
-
# Read about the other Content-* HTTP headers if you'd like to
-
# provide the user with more information (such as Content-Description) in
-
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11.
-
#
-
# Also be aware that the document may be cached by proxies and browsers.
-
# The Pragma and Cache-Control headers declare how the file may be cached
-
# by intermediaries. They default to require clients to validate with
-
# the server before releasing cached responses. See
-
# http://www.mnot.net/cache_docs/ for an overview of web caching and
-
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
-
# for the Cache-Control header spec.
-
1
def send_file(path, options = {}) #:doc:
-
raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path)
-
-
options[:filename] ||= File.basename(path) unless options[:url_based_filename]
-
send_file_headers! options
-
-
self.status = options[:status] || 200
-
self.content_type = options[:content_type] if options.key?(:content_type)
-
self.response_body = File.open(path, "rb")
-
end
-
-
# Sends the given binary data to the browser. This method is similar to
-
# <tt>render :text => data</tt>, but also allows you to specify whether
-
# the browser should display the response as a file attachment (i.e. in a
-
# download dialog) or as inline data. You may also set the content type,
-
# the apparent file name, and other things.
-
#
-
# Options:
-
# * <tt>:filename</tt> - suggests a filename for the browser to use.
-
# * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify
-
# either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json
-
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
-
# Valid values are 'inline' and 'attachment' (default).
-
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
-
#
-
# Generic data download:
-
#
-
# send_data buffer
-
#
-
# Download a dynamically-generated tarball:
-
#
-
# send_data generate_tgz('dir'), :filename => 'dir.tgz'
-
#
-
# Display an image Active Record in the browser:
-
#
-
# send_data image.data, :type => image.content_type, :disposition => 'inline'
-
#
-
# See +send_file+ for more information on HTTP Content-* headers and caching.
-
1
def send_data(data, options = {}) #:doc:
-
send_file_headers! options.dup
-
render options.slice(:status, :content_type).merge(:text => data)
-
end
-
-
1
private
-
1
def send_file_headers!(options)
-
options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options))
-
[:type, :disposition].each do |arg|
-
raise ArgumentError, ":#{arg} option required" if options[arg].nil?
-
end
-
-
disposition = options[:disposition]
-
disposition += %(; filename="#{options[:filename]}") if options[:filename]
-
-
content_type = options[:type]
-
-
if content_type.is_a?(Symbol)
-
extension = Mime[content_type]
-
raise ArgumentError, "Unknown MIME type #{options[:type]}" unless extension
-
self.content_type = extension
-
else
-
self.content_type = content_type
-
end
-
-
headers.merge!(
-
'Content-Disposition' => disposition,
-
'Content-Transfer-Encoding' => 'binary'
-
)
-
-
response.sending_file = true
-
-
# Fix a problem with IE 6.0 on opening downloaded files:
-
# If Cache-Control: no-cache is set (which Rails does by default),
-
# IE removes the file it just downloaded from its cache immediately
-
# after it displays the "open/save" dialog, which means that if you
-
# hit "open" the file isn't there anymore when the application that
-
# is called for handling the download is run, so let's workaround that
-
response.cache_control[:public] ||= false
-
end
-
end
-
end
-
1
module ActionController
-
1
class ActionControllerError < StandardError #:nodoc:
-
end
-
-
1
class RenderError < ActionControllerError #:nodoc:
-
end
-
-
1
class RoutingError < ActionControllerError #:nodoc:
-
1
attr_reader :failures
-
1
def initialize(message, failures=[])
-
super(message)
-
@failures = failures
-
end
-
end
-
-
1
class MethodNotAllowed < ActionControllerError #:nodoc:
-
1
attr_reader :allowed_methods
-
-
1
def initialize(*allowed_methods)
-
super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.")
-
end
-
end
-
-
1
class NotImplemented < MethodNotAllowed #:nodoc:
-
end
-
-
1
class UnknownController < ActionControllerError #:nodoc:
-
end
-
-
1
class MissingFile < ActionControllerError #:nodoc:
-
end
-
-
1
class RenderError < ActionControllerError #:nodoc:
-
end
-
-
1
class SessionOverflowError < ActionControllerError #:nodoc:
-
1
DEFAULT_MESSAGE = 'Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.'
-
-
1
def initialize(message = nil)
-
super(message || DEFAULT_MESSAGE)
-
end
-
end
-
-
1
class UnknownHttpMethod < ActionControllerError #:nodoc:
-
end
-
end
-
1
module ActionController #:nodoc:
-
1
module Flash
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
delegate :flash, :to => :request
-
1
delegate :alert, :notice, :to => "request.flash"
-
1
helper_method :alert, :notice
-
end
-
-
1
protected
-
1
def redirect_to(options = {}, response_status_and_flash = {}) #:doc:
-
if alert = response_status_and_flash.delete(:alert)
-
flash[:alert] = alert
-
end
-
-
if notice = response_status_and_flash.delete(:notice)
-
flash[:notice] = notice
-
end
-
-
if other_flashes = response_status_and_flash.delete(:flash)
-
flash.update(other_flashes)
-
end
-
-
super(options, response_status_and_flash)
-
end
-
end
-
end
-
1
module ActionController
-
# This module provides a method which will redirects browser to use HTTPS
-
# protocol. This will ensure that user's sensitive information will be
-
# transferred safely over the internet. You _should_ always force browser
-
# to use HTTPS when you're transferring sensitive information such as
-
# user authentication, account information, or credit card information.
-
#
-
# Note that if you really concern about your application safety, you might
-
# consider using +config.force_ssl+ in your configuration config file instead.
-
# That will ensure all the data transferred via HTTPS protocol and prevent
-
# user from getting session hijacked when accessing the site under unsecured
-
# HTTP protocol.
-
1
module ForceSSL
-
1
extend ActiveSupport::Concern
-
1
include AbstractController::Callbacks
-
-
1
module ClassMethods
-
# Force the request to this particular controller or specified actions to be
-
# under HTTPS protocol.
-
#
-
# Note that this method will not be effective on development environment.
-
#
-
# ==== Options
-
# * <tt>only</tt> - The callback should be run only for this action
-
# * <tt>except<tt> - The callback should be run for all actions except this action
-
1
def force_ssl(options = {})
-
before_filter(options) do
-
if !request.ssl? && !Rails.env.development?
-
redirect_to :protocol => 'https://', :status => :moved_permanently
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module ActionController
-
1
module Head
-
1
extend ActiveSupport::Concern
-
-
# Return a response that has no content (merely headers). The options
-
# argument is interpreted to be a hash of header names and values.
-
# This allows you to easily return a response that consists only of
-
# significant headers:
-
#
-
# head :created, :location => person_path(@person)
-
#
-
# It can also be used to return exceptional conditions:
-
#
-
# return head(:method_not_allowed) unless request.post?
-
# return head(:bad_request) unless valid_request?
-
# render
-
1
def head(status, options = {})
-
options, status = status, nil if status.is_a?(Hash)
-
status ||= options.delete(:status) || :ok
-
location = options.delete(:location)
-
-
options.each do |key, value|
-
headers[key.to_s.dasherize.split('-').each { |v| v[0] = v[0].chr.upcase }.join('-')] = value.to_s
-
end
-
-
self.status = status
-
self.location = url_for(location) if location
-
self.content_type = Mime[formats.first] if formats
-
self.response_body = " "
-
end
-
end
-
end
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_support/core_ext/class/attribute'
-
-
1
module ActionController
-
# The \Rails framework provides a large number of helpers for working with assets, dates, forms,
-
# numbers and model objects, to name a few. These helpers are available to all templates
-
# by default.
-
#
-
# In addition to using the standard template helpers provided, creating custom helpers to
-
# extract complicated logic or reusable functionality is strongly encouraged. By default, the controller will
-
# include a helper whose name matches that of the controller, e.g., <tt>MyController</tt> will automatically
-
# include <tt>MyHelper</tt>.
-
#
-
# Additional helpers can be specified using the +helper+ class method in ActionController::Base or any
-
# controller which inherits from it.
-
#
-
# ==== Examples
-
# The +to_s+ method from the \Time class can be wrapped in a helper method to display a custom message if
-
# a \Time object is blank:
-
#
-
# module FormattedTimeHelper
-
# def format_time(time, format=:long, blank_message=" ")
-
# time.blank? ? blank_message : time.to_s(format)
-
# end
-
# end
-
#
-
# FormattedTimeHelper can now be included in a controller, using the +helper+ class method:
-
#
-
# class EventsController < ActionController::Base
-
# helper FormattedTimeHelper
-
# def index
-
# @events = Event.find(:all)
-
# end
-
# end
-
#
-
# Then, in any view rendered by <tt>EventController</tt>, the <tt>format_time</tt> method can be called:
-
#
-
# <% @events.each do |event| -%>
-
# <p>
-
# <%= format_time(event.time, :short, "N/A") %> | <%= event.name %>
-
# </p>
-
# <% end -%>
-
#
-
# Finally, assuming we have two event instances, one which has a time and one which does not,
-
# the output might look like this:
-
#
-
# 23 Aug 11:30 | Carolina Railhawks Soccer Match
-
# N/A | Carolina Railhaws Training Workshop
-
#
-
1
module Helpers
-
1
extend ActiveSupport::Concern
-
-
1
include AbstractController::Helpers
-
-
1
included do
-
1
config_accessor :helpers_path, :include_all_helpers
-
1
self.helpers_path ||= []
-
1
self.include_all_helpers = true
-
end
-
-
1
module ClassMethods
-
# Declares helper accessors for controller attributes. For example, the
-
# following adds new +name+ and <tt>name=</tt> instance methods to a
-
# controller and makes them available to the view:
-
# attr_accessor :name
-
# helper_attr :name
-
#
-
# ==== Parameters
-
# * <tt>attrs</tt> - Names of attributes to be converted into helpers.
-
1
def helper_attr(*attrs)
-
attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
-
end
-
-
# Provides a proxy to access helpers methods from outside the view.
-
1
def helpers
-
@helper_proxy ||= ActionView::Base.new.extend(_helpers)
-
end
-
-
# Overwrite modules_for_helpers to accept :all as argument, which loads
-
# all helpers in helpers_path.
-
#
-
# ==== Parameters
-
# * <tt>args</tt> - A list of helpers
-
#
-
# ==== Returns
-
# * <tt>array</tt> - A normalized list of modules for the list of helpers provided.
-
1
def modules_for_helpers(args)
-
5
args += all_application_helpers if args.delete(:all)
-
5
super(args)
-
end
-
-
1
def all_helpers_from_path(path)
-
1
helpers = []
-
1
Array.wrap(path).each do |_path|
-
1
extract = /^#{Regexp.quote(_path.to_s)}\/?(.*)_helper.rb$/
-
5
helpers += Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1') }
-
end
-
1
helpers.sort!
-
1
helpers.uniq!
-
1
helpers
-
end
-
-
1
private
-
# Extract helper names from files in <tt>app/helpers/**/*_helper.rb</tt>
-
1
def all_application_helpers
-
1
all_helpers_from_path(helpers_path)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/class/attribute'
-
-
1
module ActionController
-
# Adds the ability to prevent public methods on a controller to be called as actions.
-
1
module HideActions
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
class_attribute :hidden_actions
-
1
self.hidden_actions = Set.new.freeze
-
end
-
-
1
private
-
-
# Overrides AbstractController::Base#action_method? to return false if the
-
# action name is in the list of hidden actions.
-
1
def method_for_action(action_name)
-
self.class.visible_action?(action_name) && super
-
end
-
-
1
module ClassMethods
-
# Sets all of the actions passed in as hidden actions.
-
#
-
# ==== Parameters
-
# * <tt>args</tt> - A list of actions
-
1
def hide_action(*args)
-
self.hidden_actions = hidden_actions.dup.merge(args.map(&:to_s)).freeze
-
end
-
-
1
def inherited(klass)
-
8
klass.class_eval { @visible_actions = {} }
-
4
super
-
end
-
-
1
def visible_action?(action_name)
-
return @visible_actions[action_name] if @visible_actions.key?(action_name)
-
@visible_actions[action_name] = !hidden_actions.include?(action_name)
-
end
-
-
# Overrides AbstractController::Base#action_methods to remove any methods
-
# that are listed as hidden methods.
-
1
def action_methods
-
@action_methods ||= Set.new(super.reject { |name| hidden_actions.include?(name) })
-
end
-
end
-
end
-
end
-
1
require 'active_support/base64'
-
1
require 'active_support/core_ext/object/blank'
-
-
1
module ActionController
-
1
module HttpAuthentication
-
# Makes it dead easy to do HTTP \Basic and \Digest authentication.
-
#
-
# === Simple \Basic example
-
#
-
# class PostsController < ApplicationController
-
# http_basic_authenticate_with :name => "dhh", :password => "secret", :except => :index
-
#
-
# def index
-
# render :text => "Everyone can see me!"
-
# end
-
#
-
# def edit
-
# render :text => "I'm only accessible if you know the password"
-
# end
-
# end
-
#
-
# === Advanced \Basic example
-
#
-
# Here is a more advanced \Basic example where only Atom feeds and the XML API is protected by HTTP authentication,
-
# the regular HTML interface is protected by a session approach:
-
#
-
# class ApplicationController < ActionController::Base
-
# before_filter :set_account, :authenticate
-
#
-
# protected
-
# def set_account
-
# @account = Account.find_by_url_name(request.subdomains.first)
-
# end
-
#
-
# def authenticate
-
# case request.format
-
# when Mime::XML, Mime::ATOM
-
# if user = authenticate_with_http_basic { |u, p| @account.users.authenticate(u, p) }
-
# @current_user = user
-
# else
-
# request_http_basic_authentication
-
# end
-
# else
-
# if session_authenticated?
-
# @current_user = @account.users.find(session[:authenticated][:user_id])
-
# else
-
# redirect_to(login_url) and return false
-
# end
-
# end
-
# end
-
# end
-
#
-
# In your integration tests, you can do something like this:
-
#
-
# def test_access_granted_from_xml
-
# get(
-
# "/notes/1.xml", nil,
-
# 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password)
-
# )
-
#
-
# assert_equal 200, status
-
# end
-
#
-
# === Simple \Digest example
-
#
-
# require 'digest/md5'
-
# class PostsController < ApplicationController
-
# REALM = "SuperSecret"
-
# USERS = {"dhh" => "secret", #plain text password
-
# "dap" => Digest::MD5.hexdigest(["dap",REALM,"secret"].join(":")) #ha1 digest password
-
#
-
# before_filter :authenticate, :except => [:index]
-
#
-
# def index
-
# render :text => "Everyone can see me!"
-
# end
-
#
-
# def edit
-
# render :text => "I'm only accessible if you know the password"
-
# end
-
#
-
# private
-
# def authenticate
-
# authenticate_or_request_with_http_digest(REALM) do |username|
-
# USERS[username]
-
# end
-
# end
-
# end
-
#
-
# === Notes
-
#
-
# The +authenticate_or_request_with_http_digest+ block must return the user's password
-
# or the ha1 digest hash so the framework can appropriately hash to check the user's
-
# credentials. Returning +nil+ will cause authentication to fail.
-
#
-
# Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If
-
# the password file or database is compromised, the attacker would be able to use the ha1 hash to
-
# authenticate as the user at this +realm+, but would not have the user's password to try using at
-
# other sites.
-
#
-
# In rare instances, web servers or front proxies strip authorization headers before
-
# they reach your application. You can debug this situation by logging all environment
-
# variables, and check for HTTP_AUTHORIZATION, amongst others.
-
1
module Basic
-
1
extend self
-
-
1
module ControllerMethods
-
1
extend ActiveSupport::Concern
-
-
1
module ClassMethods
-
1
def http_basic_authenticate_with(options = {})
-
before_filter(options.except(:name, :password, :realm)) do
-
authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password|
-
name == options[:name] && password == options[:password]
-
end
-
end
-
end
-
end
-
-
1
def authenticate_or_request_with_http_basic(realm = "Application", &login_procedure)
-
authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm)
-
end
-
-
1
def authenticate_with_http_basic(&login_procedure)
-
HttpAuthentication::Basic.authenticate(request, &login_procedure)
-
end
-
-
1
def request_http_basic_authentication(realm = "Application")
-
HttpAuthentication::Basic.authentication_request(self, realm)
-
end
-
end
-
-
1
def authenticate(request, &login_procedure)
-
unless request.authorization.blank?
-
login_procedure.call(*user_name_and_password(request))
-
end
-
end
-
-
1
def user_name_and_password(request)
-
decode_credentials(request).split(/:/, 2)
-
end
-
-
1
def decode_credentials(request)
-
ActiveSupport::Base64.decode64(request.authorization.split(' ', 2).last || '')
-
end
-
-
1
def encode_credentials(user_name, password)
-
"Basic #{ActiveSupport::Base64.encode64("#{user_name}:#{password}")}"
-
end
-
-
1
def authentication_request(controller, realm)
-
controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.gsub(/"/, "")}")
-
controller.response_body = "HTTP Basic: Access denied.\n"
-
controller.status = 401
-
end
-
end
-
-
1
module Digest
-
1
extend self
-
-
1
module ControllerMethods
-
1
def authenticate_or_request_with_http_digest(realm = "Application", &password_procedure)
-
authenticate_with_http_digest(realm, &password_procedure) || request_http_digest_authentication(realm)
-
end
-
-
# Authenticate with HTTP Digest, returns true or false
-
1
def authenticate_with_http_digest(realm = "Application", &password_procedure)
-
HttpAuthentication::Digest.authenticate(request, realm, &password_procedure)
-
end
-
-
# Render output including the HTTP Digest authentication header
-
1
def request_http_digest_authentication(realm = "Application", message = nil)
-
HttpAuthentication::Digest.authentication_request(self, realm, message)
-
end
-
end
-
-
# Returns false on a valid response, true otherwise
-
1
def authenticate(request, realm, &password_procedure)
-
request.authorization && validate_digest_response(request, realm, &password_procedure)
-
end
-
-
# Returns false unless the request credentials response value matches the expected value.
-
# First try the password as a ha1 digest password. If this fails, then try it as a plain
-
# text password.
-
1
def validate_digest_response(request, realm, &password_procedure)
-
secret_key = secret_token(request)
-
credentials = decode_credentials_header(request)
-
valid_nonce = validate_nonce(secret_key, request, credentials[:nonce])
-
-
if valid_nonce && realm == credentials[:realm] && opaque(secret_key) == credentials[:opaque]
-
password = password_procedure.call(credentials[:username])
-
return false unless password
-
-
method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD']
-
uri = credentials[:uri][0,1] == '/' ? request.fullpath : request.url
-
-
[true, false].any? do |password_is_ha1|
-
expected = expected_response(method, uri, credentials, password, password_is_ha1)
-
expected == credentials[:response]
-
end
-
end
-
end
-
-
# Returns the expected response for a request of +http_method+ to +uri+ with the decoded +credentials+ and the expected +password+
-
# Optional parameter +password_is_ha1+ is set to +true+ by default, since best practice is to store ha1 digest instead
-
# of a plain-text password.
-
1
def expected_response(http_method, uri, credentials, password, password_is_ha1=true)
-
ha1 = password_is_ha1 ? password : ha1(credentials, password)
-
ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(':'))
-
::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(':'))
-
end
-
-
1
def ha1(credentials, password)
-
::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(':'))
-
end
-
-
1
def encode_credentials(http_method, credentials, password, password_is_ha1)
-
credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password, password_is_ha1)
-
"Digest " + credentials.sort_by {|x| x[0].to_s }.map {|v| "#{v[0]}='#{v[1]}'" }.join(', ')
-
end
-
-
1
def decode_credentials_header(request)
-
decode_credentials(request.authorization)
-
end
-
-
1
def decode_credentials(header)
-
Hash[header.to_s.gsub(/^Digest\s+/,'').split(',').map do |pair|
-
key, value = pair.split('=', 2)
-
[key.strip.to_sym, value.to_s.gsub(/^"|"$/,'').gsub(/'/, '')]
-
end]
-
end
-
-
1
def authentication_header(controller, realm)
-
secret_key = secret_token(controller.request)
-
nonce = self.nonce(secret_key)
-
opaque = opaque(secret_key)
-
controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce}", opaque="#{opaque}")
-
end
-
-
1
def authentication_request(controller, realm, message = nil)
-
message ||= "HTTP Digest: Access denied.\n"
-
authentication_header(controller, realm)
-
controller.response_body = message
-
controller.status = 401
-
end
-
-
1
def secret_token(request)
-
secret = request.env["action_dispatch.secret_token"]
-
raise "You must set config.secret_token in your app's config" if secret.blank?
-
secret
-
end
-
-
# Uses an MD5 digest based on time to generate a value to be used only once.
-
#
-
# A server-specified data string which should be uniquely generated each time a 401 response is made.
-
# It is recommended that this string be base64 or hexadecimal data.
-
# Specifically, since the string is passed in the header lines as a quoted string, the double-quote character is not allowed.
-
#
-
# The contents of the nonce are implementation dependent.
-
# The quality of the implementation depends on a good choice.
-
# A nonce might, for example, be constructed as the base 64 encoding of
-
#
-
# => time-stamp H(time-stamp ":" ETag ":" private-key)
-
#
-
# where time-stamp is a server-generated time or other non-repeating value,
-
# ETag is the value of the HTTP ETag header associated with the requested entity,
-
# and private-key is data known only to the server.
-
# With a nonce of this form a server would recalculate the hash portion after receiving the client authentication header and
-
# reject the request if it did not match the nonce from that header or
-
# if the time-stamp value is not recent enough. In this way the server can limit the time of the nonce's validity.
-
# The inclusion of the ETag prevents a replay request for an updated version of the resource.
-
# (Note: including the IP address of the client in the nonce would appear to offer the server the ability
-
# to limit the reuse of the nonce to the same client that originally got it.
-
# However, that would break proxy farms, where requests from a single user often go through different proxies in the farm.
-
# Also, IP address spoofing is not that hard.)
-
#
-
# An implementation might choose not to accept a previously used nonce or a previously used digest, in order to
-
# protect against a replay attack. Or, an implementation might choose to use one-time nonces or digests for
-
# POST or PUT requests and a time-stamp for GET requests. For more details on the issues involved see Section 4
-
# of this document.
-
#
-
# The nonce is opaque to the client. Composed of Time, and hash of Time with secret
-
# key from the Rails session secret generated upon creation of project. Ensures
-
# the time cannot be modified by client.
-
1
def nonce(secret_key, time = Time.now)
-
t = time.to_i
-
hashed = [t, secret_key]
-
digest = ::Digest::MD5.hexdigest(hashed.join(":"))
-
ActiveSupport::Base64.encode64("#{t}:#{digest}").gsub("\n", '')
-
end
-
-
# Might want a shorter timeout depending on whether the request
-
# is a PUT or POST, and if client is browser or web service.
-
# Can be much shorter if the Stale directive is implemented. This would
-
# allow a user to use new nonce without prompting user again for their
-
# username and password.
-
1
def validate_nonce(secret_key, request, value, seconds_to_timeout=5*60)
-
t = ActiveSupport::Base64.decode64(value).split(":").first.to_i
-
nonce(secret_key, t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout
-
end
-
-
# Opaque based on random generation - but changing each request?
-
1
def opaque(secret_key)
-
::Digest::MD5.hexdigest(secret_key)
-
end
-
-
end
-
-
# Makes it dead easy to do HTTP Token authentication.
-
#
-
# Simple Token example:
-
#
-
# class PostsController < ApplicationController
-
# TOKEN = "secret"
-
#
-
# before_filter :authenticate, :except => [ :index ]
-
#
-
# def index
-
# render :text => "Everyone can see me!"
-
# end
-
#
-
# def edit
-
# render :text => "I'm only accessible if you know the password"
-
# end
-
#
-
# private
-
# def authenticate
-
# authenticate_or_request_with_http_token do |token, options|
-
# token == TOKEN
-
# end
-
# end
-
# end
-
#
-
#
-
# Here is a more advanced Token example where only Atom feeds and the XML API is protected by HTTP token authentication,
-
# the regular HTML interface is protected by a session approach:
-
#
-
# class ApplicationController < ActionController::Base
-
# before_filter :set_account, :authenticate
-
#
-
# protected
-
# def set_account
-
# @account = Account.find_by_url_name(request.subdomains.first)
-
# end
-
#
-
# def authenticate
-
# case request.format
-
# when Mime::XML, Mime::ATOM
-
# if user = authenticate_with_http_token { |t, o| @account.users.authenticate(t, o) }
-
# @current_user = user
-
# else
-
# request_http_token_authentication
-
# end
-
# else
-
# if session_authenticated?
-
# @current_user = @account.users.find(session[:authenticated][:user_id])
-
# else
-
# redirect_to(login_url) and return false
-
# end
-
# end
-
# end
-
# end
-
#
-
#
-
# In your integration tests, you can do something like this:
-
#
-
# def test_access_granted_from_xml
-
# get(
-
# "/notes/1.xml", nil,
-
# :authorization => ActionController::HttpAuthentication::Token.encode_credentials(users(:dhh).token)
-
# )
-
#
-
# assert_equal 200, status
-
# end
-
#
-
#
-
# On shared hosts, Apache sometimes doesn't pass authentication headers to
-
# FCGI instances. If your environment matches this description and you cannot
-
# authenticate, try this rule in your Apache setup:
-
#
-
# RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L]
-
1
module Token
-
1
extend self
-
-
1
module ControllerMethods
-
1
def authenticate_or_request_with_http_token(realm = "Application", &login_procedure)
-
authenticate_with_http_token(&login_procedure) || request_http_token_authentication(realm)
-
end
-
-
1
def authenticate_with_http_token(&login_procedure)
-
Token.authenticate(self, &login_procedure)
-
end
-
-
1
def request_http_token_authentication(realm = "Application")
-
Token.authentication_request(self, realm)
-
end
-
end
-
-
# If token Authorization header is present, call the login procedure with
-
# the present token and options.
-
#
-
# controller - ActionController::Base instance for the current request.
-
# login_procedure - Proc to call if a token is present. The Proc should
-
# take 2 arguments:
-
# authenticate(controller) { |token, options| ... }
-
#
-
# Returns the return value of `&login_procedure` if a token is found.
-
# Returns nil if no token is found.
-
1
def authenticate(controller, &login_procedure)
-
token, options = token_and_options(controller.request)
-
unless token.blank?
-
login_procedure.call(token, options)
-
end
-
end
-
-
# Parses the token and options out of the token authorization header. If
-
# the header looks like this:
-
# Authorization: Token token="abc", nonce="def"
-
# Then the returned token is "abc", and the options is {:nonce => "def"}
-
#
-
# request - ActionDispatch::Request instance with the current headers.
-
#
-
# Returns an Array of [String, Hash] if a token is present.
-
# Returns nil if no token is found.
-
1
def token_and_options(request)
-
if header = request.authorization.to_s[/^Token (.*)/]
-
values = Hash[$1.split(',').map do |value|
-
value.strip! # remove any spaces between commas and values
-
key, value = value.split(/\=\"?/) # split key=value pairs
-
value.chomp!('"') # chomp trailing " in value
-
value.gsub!(/\\\"/, '"') # unescape remaining quotes
-
[key, value]
-
end]
-
[values.delete("token"), values.with_indifferent_access]
-
end
-
end
-
-
# Encodes the given token and options into an Authorization header value.
-
#
-
# token - String token.
-
# options - optional Hash of the options.
-
#
-
# Returns String.
-
1
def encode_credentials(token, options = {})
-
values = ["token=#{token.to_s.inspect}"] + options.map do |key, value|
-
"#{key}=#{value.to_s.inspect}"
-
end
-
"Token #{values * ", "}"
-
end
-
-
# Sets a WWW-Authenticate to let the client know a token is desired.
-
#
-
# controller - ActionController::Base instance for the outgoing response.
-
# realm - String realm to use in the header.
-
#
-
# Returns nothing.
-
1
def authentication_request(controller, realm)
-
controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.gsub(/"/, "")}")
-
controller.__send__ :render, :text => "HTTP Token: Access denied.\n", :status => :unauthorized
-
end
-
end
-
end
-
end
-
1
module ActionController
-
1
module ImplicitRender
-
1
def send_action(method, *args)
-
ret = super
-
default_render unless response_body
-
ret
-
end
-
-
1
def default_render(*args)
-
render(*args)
-
end
-
-
1
def method_for_action(action_name)
-
super || if template_exists?(action_name.to_s, _prefixes)
-
"default_render"
-
end
-
end
-
end
-
end
-
1
require 'benchmark'
-
1
require 'abstract_controller/logger'
-
-
1
module ActionController
-
# Adds instrumentation to several ends in ActionController::Base. It also provides
-
# some hooks related with process_action, this allows an ORM like Active Record
-
# and/or DataMapper to plug in ActionController and show related information.
-
#
-
# Check ActiveRecord::Railties::ControllerRuntime for an example.
-
1
module Instrumentation
-
1
extend ActiveSupport::Concern
-
-
1
include AbstractController::Logger
-
-
1
attr_internal :view_runtime
-
-
1
def process_action(*args)
-
raw_payload = {
-
:controller => self.class.name,
-
:action => self.action_name,
-
:params => request.filtered_parameters,
-
:format => request.format.try(:ref),
-
:method => request.method,
-
:path => (request.fullpath rescue "unknown")
-
}
-
-
ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup)
-
-
ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
-
result = super
-
payload[:status] = response.status
-
append_info_to_payload(payload)
-
result
-
end
-
end
-
-
1
def render(*args)
-
render_output = nil
-
self.view_runtime = cleanup_view_runtime do
-
Benchmark.ms { render_output = super }
-
end
-
render_output
-
end
-
-
1
def send_file(path, options={})
-
ActiveSupport::Notifications.instrument("send_file.action_controller",
-
options.merge(:path => path)) do
-
super
-
end
-
end
-
-
1
def send_data(data, options = {})
-
ActiveSupport::Notifications.instrument("send_data.action_controller", options) do
-
super
-
end
-
end
-
-
1
def redirect_to(*args)
-
ActiveSupport::Notifications.instrument("redirect_to.action_controller") do |payload|
-
result = super
-
payload[:status] = self.status
-
payload[:location] = self.location
-
result
-
end
-
end
-
-
1
protected
-
-
# A hook which allows you to clean up any time taken into account in
-
# views wrongly, like database querying time.
-
#
-
# def cleanup_view_runtime
-
# super - time_taken_in_something_expensive
-
# end
-
#
-
# :api: plugin
-
1
def cleanup_view_runtime #:nodoc:
-
yield
-
end
-
-
# Every time after an action is processed, this method is invoked
-
# with the payload, so you can add more information.
-
# :api: plugin
-
1
def append_info_to_payload(payload) #:nodoc:
-
payload[:view_runtime] = view_runtime
-
end
-
-
1
module ClassMethods
-
# A hook which allows other frameworks to log what happened during
-
# controller process action. This method should return an array
-
# with the messages to be added.
-
# :api: plugin
-
1
def log_process_action(payload) #:nodoc:
-
messages, view_runtime = [], payload[:view_runtime]
-
messages << ("Views: %.1fms" % view_runtime.to_f) if view_runtime
-
messages
-
end
-
end
-
end
-
end
-
1
require 'abstract_controller/collector'
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/object/inclusion'
-
-
1
module ActionController #:nodoc:
-
1
module MimeResponds
-
1
extend ActiveSupport::Concern
-
-
1
include ActionController::ImplicitRender
-
-
1
included do
-
1
class_attribute :responder, :mimes_for_respond_to
-
1
self.responder = ActionController::Responder
-
1
clear_respond_to
-
end
-
-
1
module ClassMethods
-
# Defines mime types that are rendered by default when invoking
-
# <tt>respond_with</tt>.
-
#
-
# Examples:
-
#
-
# respond_to :html, :xml, :json
-
#
-
# Specifies that all actions in the controller respond to requests
-
# for <tt>:html</tt>, <tt>:xml</tt> and <tt>:json</tt>.
-
#
-
# To specify on per-action basis, use <tt>:only</tt> and
-
# <tt>:except</tt> with an array of actions or a single action:
-
#
-
# respond_to :html
-
# respond_to :xml, :json, :except => [ :edit ]
-
#
-
# This specifies that all actions respond to <tt>:html</tt>
-
# and all actions except <tt>:edit</tt> respond to <tt>:xml</tt> and
-
# <tt>:json</tt>.
-
#
-
# respond_to :json, :only => :create
-
#
-
# This specifies that the <tt>:create</tt> action and no other responds
-
# to <tt>:json</tt>.
-
1
def respond_to(*mimes)
-
options = mimes.extract_options!
-
-
only_actions = Array(options.delete(:only))
-
except_actions = Array(options.delete(:except))
-
-
new = mimes_for_respond_to.dup
-
mimes.each do |mime|
-
mime = mime.to_sym
-
new[mime] = {}
-
new[mime][:only] = only_actions unless only_actions.empty?
-
new[mime][:except] = except_actions unless except_actions.empty?
-
end
-
self.mimes_for_respond_to = new.freeze
-
end
-
-
# Clear all mime types in <tt>respond_to</tt>.
-
#
-
1
def clear_respond_to
-
1
self.mimes_for_respond_to = ActiveSupport::OrderedHash.new.freeze
-
end
-
end
-
-
# Without web-service support, an action which collects the data for displaying a list of people
-
# might look something like this:
-
#
-
# def index
-
# @people = Person.all
-
# end
-
#
-
# Here's the same action, with web-service support baked in:
-
#
-
# def index
-
# @people = Person.all
-
#
-
# respond_to do |format|
-
# format.html
-
# format.xml { render :xml => @people.to_xml }
-
# end
-
# end
-
#
-
# What that says is, "if the client wants HTML in response to this action, just respond as we
-
# would have before, but if the client wants XML, return them the list of people in XML format."
-
# (Rails determines the desired response format from the HTTP Accept header submitted by the client.)
-
#
-
# Supposing you have an action that adds a new person, optionally creating their company
-
# (by name) if it does not already exist, without web-services, it might look like this:
-
#
-
# def create
-
# @company = Company.find_or_create_by_name(params[:company][:name])
-
# @person = @company.people.create(params[:person])
-
#
-
# redirect_to(person_list_url)
-
# end
-
#
-
# Here's the same action, with web-service support baked in:
-
#
-
# def create
-
# company = params[:person].delete(:company)
-
# @company = Company.find_or_create_by_name(company[:name])
-
# @person = @company.people.create(params[:person])
-
#
-
# respond_to do |format|
-
# format.html { redirect_to(person_list_url) }
-
# format.js
-
# format.xml { render :xml => @person.to_xml(:include => @company) }
-
# end
-
# end
-
#
-
# If the client wants HTML, we just redirect them back to the person list. If they want JavaScript,
-
# then it is an Ajax request and we render the JavaScript template associated with this action.
-
# Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also
-
# include the person's company in the rendered XML, so you get something like this:
-
#
-
# <person>
-
# <id>...</id>
-
# ...
-
# <company>
-
# <id>...</id>
-
# <name>...</name>
-
# ...
-
# </company>
-
# </person>
-
#
-
# Note, however, the extra bit at the top of that action:
-
#
-
# company = params[:person].delete(:company)
-
# @company = Company.find_or_create_by_name(company[:name])
-
#
-
# This is because the incoming XML document (if a web-service request is in process) can only contain a
-
# single root-node. So, we have to rearrange things so that the request looks like this (url-encoded):
-
#
-
# person[name]=...&person[company][name]=...&...
-
#
-
# And, like this (xml-encoded):
-
#
-
# <person>
-
# <name>...</name>
-
# <company>
-
# <name>...</name>
-
# </company>
-
# </person>
-
#
-
# In other words, we make the request so that it operates on a single entity's person. Then, in the action,
-
# we extract the company data from the request, find or create the company, and then create the new person
-
# with the remaining data.
-
#
-
# Note that you can define your own XML parameter parser which would allow you to describe multiple entities
-
# in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow
-
# and accept Rails' defaults, life will be much easier.
-
#
-
# If you need to use a MIME type which isn't supported by default, you can register your own handlers in
-
# config/initializers/mime_types.rb as follows.
-
#
-
# Mime::Type.register "image/jpg", :jpg
-
#
-
# Respond to also allows you to specify a common block for different formats by using any:
-
#
-
# def index
-
# @people = Person.all
-
#
-
# respond_to do |format|
-
# format.html
-
# format.any(:xml, :json) { render request.format.to_sym => @people }
-
# end
-
# end
-
#
-
# In the example above, if the format is xml, it will render:
-
#
-
# render :xml => @people
-
#
-
# Or if the format is json:
-
#
-
# render :json => @people
-
#
-
# Since this is a common pattern, you can use the class method respond_to
-
# with the respond_with method to have the same results:
-
#
-
# class PeopleController < ApplicationController
-
# respond_to :html, :xml, :json
-
#
-
# def index
-
# @people = Person.all
-
# respond_with(@person)
-
# end
-
# end
-
#
-
# Be sure to check respond_with and respond_to documentation for more examples.
-
#
-
1
def respond_to(*mimes, &block)
-
raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
-
-
if response = retrieve_response_from_mimes(mimes, &block)
-
response.call(nil)
-
end
-
end
-
-
# respond_with wraps a resource around a responder for default representation.
-
# First it invokes respond_to, if a response cannot be found (ie. no block
-
# for the request was given and template was not available), it instantiates
-
# an ActionController::Responder with the controller and resource.
-
#
-
# ==== Example
-
#
-
# def index
-
# @users = User.all
-
# respond_with(@users)
-
# end
-
#
-
# It also accepts a block to be given. It's used to overwrite a default
-
# response:
-
#
-
# def create
-
# @user = User.new(params[:user])
-
# flash[:notice] = "User was successfully created." if @user.save
-
#
-
# respond_with(@user) do |format|
-
# format.html { render }
-
# end
-
# end
-
#
-
# All options given to respond_with are sent to the underlying responder,
-
# except for the option :responder itself. Since the responder interface
-
# is quite simple (it just needs to respond to call), you can even give
-
# a proc to it.
-
#
-
# In order to use respond_with, first you need to declare the formats your
-
# controller responds to in the class level with a call to <tt>respond_to</tt>.
-
#
-
1
def respond_with(*resources, &block)
-
raise "In order to use respond_with, first you need to declare the formats your " <<
-
"controller responds to in the class level" if self.class.mimes_for_respond_to.empty?
-
-
if response = retrieve_response_from_mimes(&block)
-
options = resources.size == 1 ? {} : resources.extract_options!
-
options.merge!(:default_response => response)
-
(options.delete(:responder) || self.class.responder).call(self, resources, options)
-
end
-
end
-
-
1
protected
-
-
# Collect mimes declared in the class method respond_to valid for the
-
# current action.
-
#
-
1
def collect_mimes_from_class_level #:nodoc:
-
action = action_name.to_sym
-
-
self.class.mimes_for_respond_to.keys.select do |mime|
-
config = self.class.mimes_for_respond_to[mime]
-
-
if config[:except]
-
!action.in?(config[:except])
-
elsif config[:only]
-
action.in?(config[:only])
-
else
-
true
-
end
-
end
-
end
-
-
# Collects mimes and return the response for the negotiated format. Returns
-
# nil if :not_acceptable was sent to the client.
-
#
-
1
def retrieve_response_from_mimes(mimes=nil, &block) #:nodoc:
-
mimes ||= collect_mimes_from_class_level
-
collector = Collector.new(mimes) { |options| default_render(options || {}) }
-
block.call(collector) if block_given?
-
-
if format = request.negotiate_mime(collector.order)
-
self.content_type ||= format.to_s
-
lookup_context.freeze_formats([format.to_sym])
-
collector.response_for(format)
-
else
-
head :not_acceptable
-
nil
-
end
-
end
-
-
1
class Collector #:nodoc:
-
1
include AbstractController::Collector
-
1
attr_accessor :order
-
-
1
def initialize(mimes, &block)
-
@order, @responses, @default_response = [], {}, block
-
mimes.each { |mime| send(mime) }
-
end
-
-
1
def any(*args, &block)
-
if args.any?
-
args.each { |type| send(type, &block) }
-
else
-
custom(Mime::ALL, &block)
-
end
-
end
-
1
alias :all :any
-
-
1
def custom(mime_type, &block)
-
mime_type = Mime::Type.lookup(mime_type.to_s) unless mime_type.is_a?(Mime::Type)
-
@order << mime_type
-
@responses[mime_type] ||= block
-
end
-
-
1
def response_for(mime)
-
@responses[mime] || @responses[Mime::ALL] || @default_response
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/hash/slice'
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_support/core_ext/module/anonymous'
-
1
require 'action_dispatch/http/mime_types'
-
-
1
module ActionController
-
# Wraps parameters hash into nested hash. This will allow client to submit
-
# POST request without having to specify a root element in it.
-
#
-
# This functionality is enabled in +config/initializers/wrap_parameters.rb+
-
# and can be customized. If you are upgrading to Rails 3.1, this file will
-
# need to be created for the functionality to be enabled.
-
#
-
# You could also turn it on per controller by setting the format array to
-
# non-empty array:
-
#
-
# class UsersController < ApplicationController
-
# wrap_parameters :format => [:json, :xml]
-
# end
-
#
-
# If you enable +ParamsWrapper+ for +:json+ format. Instead of having to
-
# send JSON parameters like this:
-
#
-
# {"user": {"name": "Konata"}}
-
#
-
# You can now just send a parameters like this:
-
#
-
# {"name": "Konata"}
-
#
-
# And it will be wrapped into a nested hash with the key name matching
-
# controller's name. For example, if you're posting to +UsersController+,
-
# your new +params+ hash will look like this:
-
#
-
# {"name" => "Konata", "user" => {"name" => "Konata"}}
-
#
-
# You can also specify the key in which the parameters should be wrapped to,
-
# and also the list of attributes it should wrap by using either +:include+ or
-
# +:exclude+ options like this:
-
#
-
# class UsersController < ApplicationController
-
# wrap_parameters :person, :include => [:username, :password]
-
# end
-
#
-
# If you're going to pass the parameters to an +ActiveModel+ object (such as
-
# +User.new(params[:user])+), you might consider passing the model class to
-
# the method instead. The +ParamsWrapper+ will actually try to determine the
-
# list of attribute names from the model and only wrap those attributes:
-
#
-
# class UsersController < ApplicationController
-
# wrap_parameters Person
-
# end
-
#
-
# You still could pass +:include+ and +:exclude+ to set the list of attributes
-
# you want to wrap.
-
#
-
# By default, if you don't specify the key in which the parameters would be
-
# wrapped to, +ParamsWrapper+ will actually try to determine if there's
-
# a model related to it or not. This controller, for example:
-
#
-
# class Admin::UsersController < ApplicationController
-
# end
-
#
-
# will try to check if +Admin::User+ or +User+ model exists, and use it to
-
# determine the wrapper key respectively. If both models don't exist,
-
# it will then fallback to use +user+ as the key.
-
1
module ParamsWrapper
-
1
extend ActiveSupport::Concern
-
-
1
EXCLUDE_PARAMETERS = %w(authenticity_token _method utf8)
-
-
1
included do
-
1
class_attribute :_wrapper_options
-
1
self._wrapper_options = { :format => [] }
-
end
-
-
1
module ClassMethods
-
# Sets the name of the wrapper key, or the model which +ParamsWrapper+
-
# would use to determine the attribute names from.
-
#
-
# ==== Examples
-
# wrap_parameters :format => :xml
-
# # enables the parmeter wrapper for XML format
-
#
-
# wrap_parameters :person
-
# # wraps parameters into +params[:person]+ hash
-
#
-
# wrap_parameters Person
-
# # wraps parameters by determining the wrapper key from Person class
-
# (+person+, in this case) and the list of attribute names
-
#
-
# wrap_parameters :include => [:username, :title]
-
# # wraps only +:username+ and +:title+ attributes from parameters.
-
#
-
# wrap_parameters false
-
# # disables parameters wrapping for this controller altogether.
-
#
-
# ==== Options
-
# * <tt>:format</tt> - The list of formats in which the parameters wrapper
-
# will be enabled.
-
# * <tt>:include</tt> - The list of attribute names which parameters wrapper
-
# will wrap into a nested hash.
-
# * <tt>:exclude</tt> - The list of attribute names which parameters wrapper
-
# will exclude from a nested hash.
-
1
def wrap_parameters(name_or_model_or_options, options = {})
-
1
model = nil
-
-
1
case name_or_model_or_options
-
when Hash
-
1
options = name_or_model_or_options
-
when false
-
options = options.merge(:format => [])
-
when Symbol, String
-
options = options.merge(:name => name_or_model_or_options)
-
else
-
model = name_or_model_or_options
-
end
-
-
1
_set_wrapper_defaults(_wrapper_options.slice(:format).merge(options), model)
-
end
-
-
# Sets the default wrapper key or model which will be used to determine
-
# wrapper key and attribute names. Will be called automatically when the
-
# module is inherited.
-
1
def inherited(klass)
-
4
if klass._wrapper_options[:format].present?
-
4
klass._set_wrapper_defaults(klass._wrapper_options.slice(:format))
-
end
-
4
super
-
end
-
-
1
protected
-
-
# Determine the wrapper model from the controller's name. By convention,
-
# this could be done by trying to find the defined model that has the
-
# same singularize name as the controller. For example, +UsersController+
-
# will try to find if the +User+ model exists.
-
#
-
# This method also does namespace lookup. Foo::Bar::UsersController will
-
# try to find Foo::Bar::User, Foo::User and finally User.
-
1
def _default_wrap_model #:nodoc:
-
6
return nil if self.anonymous?
-
-
6
model_name = self.name.sub(/Controller$/, '').singularize
-
-
begin
-
6
model_klass = model_name.constantize
-
rescue NameError, ArgumentError => e
-
2
if e.message =~ /is not missing constant|uninitialized constant #{model_name}/
-
2
namespaces = model_name.split("::")
-
2
namespaces.delete_at(-2)
-
2
break if namespaces.last == model_name
-
model_name = namespaces.join("::")
-
else
-
raise
-
end
-
6
end until model_klass
-
-
6
model_klass
-
end
-
-
1
def _set_wrapper_defaults(options, model=nil)
-
5
options = options.dup
-
-
5
unless options[:include] || options[:exclude]
-
5
model ||= _default_wrap_model
-
5
if model.respond_to?(:attribute_names) && model.attribute_names.present?
-
2
options[:include] = model.attribute_names
-
end
-
end
-
-
5
unless options[:name] || self.anonymous?
-
5
model ||= _default_wrap_model
-
5
options[:name] = model ? model.to_s.demodulize.underscore :
-
controller_name.singularize
-
end
-
-
5
options[:include] = Array.wrap(options[:include]).collect(&:to_s) if options[:include]
-
5
options[:exclude] = Array.wrap(options[:exclude]).collect(&:to_s) if options[:exclude]
-
5
options[:format] = Array.wrap(options[:format])
-
-
5
self._wrapper_options = options
-
end
-
end
-
-
# Performs parameters wrapping upon the request. Will be called automatically
-
# by the metal call stack.
-
1
def process_action(*args)
-
if _wrapper_enabled?
-
wrapped_hash = _wrap_parameters request.request_parameters
-
wrapped_filtered_hash = _wrap_parameters request.filtered_parameters
-
-
# This will make the wrapped hash accessible from controller and view
-
request.parameters.merge! wrapped_hash
-
request.request_parameters.merge! wrapped_hash
-
-
# This will make the wrapped hash displayed in the log file
-
request.filtered_parameters.merge! wrapped_filtered_hash
-
end
-
super
-
end
-
-
1
private
-
-
# Returns the wrapper key which will use to stored wrapped parameters.
-
1
def _wrapper_key
-
_wrapper_options[:name]
-
end
-
-
# Returns the list of enabled formats.
-
1
def _wrapper_formats
-
_wrapper_options[:format]
-
end
-
-
# Returns the list of parameters which will be selected for wrapped.
-
1
def _wrap_parameters(parameters)
-
value = if include_only = _wrapper_options[:include]
-
parameters.slice(*include_only)
-
else
-
exclude = _wrapper_options[:exclude] || []
-
parameters.except(*(exclude + EXCLUDE_PARAMETERS))
-
end
-
-
{ _wrapper_key => value }
-
end
-
-
# Checks if we should perform parameters wrapping.
-
1
def _wrapper_enabled?
-
ref = request.content_mime_type.try(:ref)
-
_wrapper_formats.include?(ref) && _wrapper_key && !request.request_parameters[_wrapper_key]
-
end
-
end
-
end
-
1
require 'action_dispatch/http/request'
-
1
require 'action_dispatch/http/response'
-
-
1
module ActionController
-
1
module RackDelegation
-
1
extend ActiveSupport::Concern
-
-
1
delegate :headers, :status=, :location=, :content_type=,
-
:status, :location, :content_type, :to => "@_response"
-
-
1
def dispatch(action, request, response = ActionDispatch::Response.new)
-
@_response ||= response
-
@_response.request ||= request
-
super(action, request)
-
end
-
-
1
def response_body=(body)
-
response.body = body if response
-
super
-
end
-
-
1
def reset_session
-
@_request.reset_session
-
end
-
end
-
end
-
1
module ActionController
-
1
class RedirectBackError < AbstractController::Error #:nodoc:
-
1
DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].'
-
-
1
def initialize(message = nil)
-
super(message || DEFAULT_MESSAGE)
-
end
-
end
-
-
1
module Redirecting
-
1
extend ActiveSupport::Concern
-
-
1
include AbstractController::Logger
-
1
include ActionController::RackDelegation
-
1
include ActionController::UrlFor
-
-
# Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
-
#
-
# * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+.
-
# * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record.
-
# * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) - Is passed straight through as the target for redirection.
-
# * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string.
-
# * <tt>Proc</tt> - A block that will be executed in the controller's context. Should return any option accepted by +redirect_to+.
-
# * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
-
# Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt>
-
#
-
# Examples:
-
# redirect_to :action => "show", :id => 5
-
# redirect_to post
-
# redirect_to "http://www.rubyonrails.org"
-
# redirect_to "/images/screenshot.jpg"
-
# redirect_to articles_url
-
# redirect_to :back
-
# redirect_to proc { edit_post_url(@post) }
-
#
-
# The redirection happens as a "302 Moved" header unless otherwise specified.
-
#
-
# Examples:
-
# redirect_to post_url(@post), :status => :found
-
# redirect_to :action=>'atom', :status => :moved_permanently
-
# redirect_to post_url(@post), :status => 301
-
# redirect_to :action=>'atom', :status => 302
-
#
-
# The status code can either be a standard {HTTP Status code}[http://www.iana.org/assignments/http-status-codes] as an
-
# integer, or a symbol representing the downcased, underscored and symbolized description.
-
# Note that the status code must be a 3xx HTTP code, or redirection will not occur.
-
#
-
# It is also possible to assign a flash message as part of the redirection. There are two special accessors for the commonly used flash names
-
# +alert+ and +notice+ as well as a general purpose +flash+ bucket.
-
#
-
# Examples:
-
# redirect_to post_url(@post), :alert => "Watch it, mister!"
-
# redirect_to post_url(@post), :status=> :found, :notice => "Pay attention to the road"
-
# redirect_to post_url(@post), :status => 301, :flash => { :updated_post_id => @post.id }
-
# redirect_to { :action=>'atom' }, :alert => "Something serious happened"
-
#
-
# When using <tt>redirect_to :back</tt>, if there is no referrer, RedirectBackError will be raised. You may specify some fallback
-
# behavior for this case by rescuing RedirectBackError.
-
1
def redirect_to(options = {}, response_status = {}) #:doc:
-
raise ActionControllerError.new("Cannot redirect to nil!") if options.nil?
-
raise AbstractController::DoubleRenderError if response_body
-
-
self.status = _extract_redirect_to_status(options, response_status)
-
self.location = _compute_redirect_to_location(options)
-
self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.h(location)}\">redirected</a>.</body></html>"
-
end
-
-
1
private
-
1
def _extract_redirect_to_status(options, response_status)
-
status = if options.is_a?(Hash) && options.key?(:status)
-
Rack::Utils.status_code(options.delete(:status))
-
elsif response_status.key?(:status)
-
Rack::Utils.status_code(response_status[:status])
-
else
-
302
-
end
-
end
-
-
1
def _compute_redirect_to_location(options)
-
case options
-
# The scheme name consist of a letter followed by any combination of
-
# letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
-
# characters; and is terminated by a colon (":").
-
when %r{^\w[\w+.-]*:.*}
-
options
-
when String
-
request.protocol + request.host_with_port + options
-
when :back
-
raise RedirectBackError unless refer = request.headers["Referer"]
-
refer
-
when Proc
-
_compute_redirect_to_location options.call
-
else
-
url_for(options)
-
end.gsub(/[\r\n]/, '')
-
end
-
end
-
end
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/object/blank'
-
-
1
module ActionController
-
# See <tt>Renderers.add</tt>
-
1
def self.add_renderer(key, &block)
-
Renderers.add(key, &block)
-
end
-
-
1
module Renderers
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
class_attribute :_renderers
-
1
self._renderers = {}.freeze
-
end
-
-
1
module ClassMethods
-
1
def use_renderers(*args)
-
new = _renderers.dup
-
args.each do |key|
-
new[key] = RENDERERS[key]
-
end
-
self._renderers = new.freeze
-
end
-
1
alias use_renderer use_renderers
-
end
-
-
1
def render_to_body(options)
-
_handle_render_options(options) || super
-
end
-
-
1
def _handle_render_options(options)
-
_renderers.each do |name, value|
-
if options.key?(name.to_sym)
-
_process_options(options)
-
return send("_render_option_#{name}", options.delete(name.to_sym), options)
-
end
-
end
-
nil
-
end
-
-
# Hash of available renderers, mapping a renderer name to its proc.
-
# Default keys are :json, :js, :xml.
-
1
RENDERERS = {}
-
-
# Adds a new renderer to call within controller actions.
-
# A renderer is invoked by passing its name as an option to
-
# <tt>AbstractController::Rendering#render</tt>. To create a renderer
-
# pass it a name and a block. The block takes two arguments, the first
-
# is the value paired with its key and the second is the remaining
-
# hash of options passed to +render+.
-
#
-
# === Example
-
# Create a csv renderer:
-
#
-
# ActionController::Renderers.add :csv do |obj, options|
-
# filename = options[:filename] || 'data'
-
# str = obj.respond_to?(:to_csv) ? obj.to_csv : obj.to_s
-
# send_data str, :type => Mime::CSV,
-
# :disposition => "attachment; filename=#{filename}.csv"
-
# end
-
#
-
# Note that we used Mime::CSV for the csv mime type as it comes with Rails.
-
# For a custom renderer, you'll need to register a mime type with
-
# <tt>Mime::Type.register</tt>.
-
#
-
# To use the csv renderer in a controller action:
-
#
-
# def show
-
# @csvable = Csvable.find(params[:id])
-
# respond_to do |format|
-
# format.html
-
# format.csv { render :csv => @csvable, :filename => @csvable.name }
-
# }
-
# end
-
# To use renderers and their mime types in more concise ways, see
-
# <tt>ActionController::MimeResponds::ClassMethods.respond_to</tt> and
-
# <tt>ActionController::MimeResponds#respond_with</tt>
-
1
def self.add(key, &block)
-
3
define_method("_render_option_#{key}", &block)
-
3
RENDERERS[key] = block
-
end
-
-
1
module All
-
1
extend ActiveSupport::Concern
-
1
include Renderers
-
-
1
included do
-
1
self._renderers = RENDERERS
-
end
-
end
-
-
1
add :json do |json, options|
-
json = json.to_json(options) unless json.kind_of?(String)
-
json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
-
self.content_type ||= Mime::JSON
-
json
-
end
-
-
1
add :js do |js, options|
-
self.content_type ||= Mime::JS
-
js.respond_to?(:to_js) ? js.to_js(options) : js
-
end
-
-
1
add :xml do |xml, options|
-
self.content_type ||= Mime::XML
-
xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml
-
end
-
end
-
end
-
1
module ActionController
-
1
module Rendering
-
1
extend ActiveSupport::Concern
-
-
1
include AbstractController::Rendering
-
-
# Before processing, set the request formats in current controller formats.
-
1
def process_action(*) #:nodoc:
-
self.formats = request.formats.map { |x| x.ref }
-
super
-
end
-
-
# Check for double render errors and set the content_type after rendering.
-
1
def render(*args) #:nodoc:
-
raise ::AbstractController::DoubleRenderError if response_body
-
super
-
self.content_type ||= Mime[formats.first].to_s
-
response_body
-
end
-
-
# Overwrite render_to_string because body can now be set to a rack body.
-
1
def render_to_string(*)
-
if self.response_body = super
-
string = ""
-
response_body.each { |r| string << r }
-
string
-
end
-
ensure
-
self.response_body = nil
-
end
-
-
1
private
-
-
# Normalize arguments by catching blocks and setting them on :update.
-
1
def _normalize_args(action=nil, options={}, &blk) #:nodoc:
-
options = super
-
options[:update] = blk if block_given?
-
options
-
end
-
-
# Normalize both text and status options.
-
1
def _normalize_options(options) #:nodoc:
-
if options.key?(:text) && options[:text].respond_to?(:to_text)
-
options[:text] = options[:text].to_text
-
end
-
-
if options[:status]
-
options[:status] = Rack::Utils.status_code(options[:status])
-
end
-
-
super
-
end
-
-
# Process controller specific options, as status, content-type and location.
-
1
def _process_options(options) #:nodoc:
-
status, content_type, location = options.values_at(:status, :content_type, :location)
-
-
self.status = status if status
-
self.content_type = content_type if content_type
-
self.headers["Location"] = url_for(location) if location
-
-
super
-
end
-
end
-
end
-
1
require 'active_support/core_ext/class/attribute'
-
-
1
module ActionController #:nodoc:
-
1
class InvalidAuthenticityToken < ActionControllerError #:nodoc:
-
end
-
-
# Controller actions are protected from Cross-Site Request Forgery (CSRF) attacks
-
# by including a token in the rendered html for your application. This token is
-
# stored as a random string in the session, to which an attacker does not have
-
# access. When a request reaches your application, \Rails verifies the received
-
# token with the token in the session. Only HTML and JavaScript requests are checked,
-
# so this will not protect your XML API (presumably you'll have a different
-
# authentication scheme there anyway). Also, GET requests are not protected as these
-
# should be idempotent.
-
#
-
# CSRF protection is turned on with the <tt>protect_from_forgery</tt> method,
-
# which checks the token and resets the session if it doesn't match what was expected.
-
# A call to this method is generated for new \Rails applications by default.
-
# You can customize the error message by editing public/422.html.
-
#
-
# The token parameter is named <tt>authenticity_token</tt> by default. The name and
-
# value of this token must be added to every layout that renders forms by including
-
# <tt>csrf_meta_tags</tt> in the html +head+.
-
#
-
# Learn more about CSRF attacks and securing your application in the
-
# {Ruby on Rails Security Guide}[http://guides.rubyonrails.org/security.html].
-
1
module RequestForgeryProtection
-
1
extend ActiveSupport::Concern
-
-
1
include AbstractController::Helpers
-
1
include AbstractController::Callbacks
-
-
1
included do
-
# Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+
-
# sets it to <tt>:authenticity_token</tt> by default.
-
1
config_accessor :request_forgery_protection_token
-
1
self.request_forgery_protection_token ||= :authenticity_token
-
-
# Controls whether request forgery protection is turned on or not. Turned off by default only in test mode.
-
1
config_accessor :allow_forgery_protection
-
1
self.allow_forgery_protection = true if allow_forgery_protection.nil?
-
-
1
helper_method :form_authenticity_token
-
1
helper_method :protect_against_forgery?
-
end
-
-
1
module ClassMethods
-
# Turn on request forgery protection. Bear in mind that only non-GET, HTML/JavaScript requests are checked.
-
#
-
# Example:
-
#
-
# class FooController < ApplicationController
-
# protect_from_forgery :except => :index
-
#
-
# You can disable csrf protection on controller-by-controller basis:
-
#
-
# skip_before_filter :verify_authenticity_token
-
#
-
# It can also be disabled for specific controller actions:
-
#
-
# skip_before_filter :verify_authenticity_token, :except => [:create]
-
#
-
# Valid Options:
-
#
-
# * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call. Set which actions are verified.
-
1
def protect_from_forgery(options = {})
-
1
self.request_forgery_protection_token ||= :authenticity_token
-
1
prepend_before_filter :verify_authenticity_token, options
-
end
-
end
-
-
1
protected
-
# The actual before_filter that is used. Modify this to change how you handle unverified requests.
-
1
def verify_authenticity_token
-
unless verified_request?
-
logger.debug "WARNING: Can't verify CSRF token authenticity" if logger
-
handle_unverified_request
-
end
-
end
-
-
# This is the method that defines the application behavior when a request is found to be unverified.
-
# By default, \Rails resets the session when it finds an unverified request.
-
1
def handle_unverified_request
-
reset_session
-
end
-
-
# Returns true or false if a request is verified. Checks:
-
#
-
# * is it a GET request? Gets should be safe and idempotent
-
# * Does the form_authenticity_token match the given token value from the params?
-
# * Does the X-CSRF-Token header match the form_authenticity_token
-
1
def verified_request?
-
!protect_against_forgery? || request.get? ||
-
form_authenticity_token == params[request_forgery_protection_token] ||
-
form_authenticity_token == request.headers['X-CSRF-Token']
-
end
-
-
# Sets the token value for the current session.
-
1
def form_authenticity_token
-
session[:_csrf_token] ||= SecureRandom.base64(32)
-
end
-
-
# The form's authenticity parameter. Override to provide your own.
-
1
def form_authenticity_param
-
params[request_forgery_protection_token]
-
end
-
-
1
def protect_against_forgery?
-
allow_forgery_protection
-
end
-
end
-
end
-
1
module ActionController #:nodoc:
-
1
module Rescue
-
1
extend ActiveSupport::Concern
-
1
include ActiveSupport::Rescuable
-
-
1
def rescue_with_handler(exception)
-
if (exception.respond_to?(:original_exception) &&
-
(orig_exception = exception.original_exception) &&
-
handler_for_rescue(orig_exception))
-
exception = orig_exception
-
end
-
super(exception)
-
end
-
-
1
private
-
1
def process_action(*args)
-
super
-
rescue Exception => exception
-
rescue_with_handler(exception) || raise(exception)
-
end
-
end
-
end
-
1
require 'active_support/json'
-
-
1
module ActionController #:nodoc:
-
# Responsible for exposing a resource to different mime requests,
-
# usually depending on the HTTP verb. The responder is triggered when
-
# <code>respond_with</code> is called. The simplest case to study is a GET request:
-
#
-
# class PeopleController < ApplicationController
-
# respond_to :html, :xml, :json
-
#
-
# def index
-
# @people = Person.find(:all)
-
# respond_with(@people)
-
# end
-
# end
-
#
-
# When a request comes in, for example for an XML response, three steps happen:
-
#
-
# 1) the responder searches for a template at people/index.xml;
-
#
-
# 2) if the template is not available, it will invoke <code>#to_xml</code> on the given resource;
-
#
-
# 3) if the responder does not <code>respond_to :to_xml</code>, call <code>#to_format</code> on it.
-
#
-
# === Builtin HTTP verb semantics
-
#
-
# The default \Rails responder holds semantics for each HTTP verb. Depending on the
-
# content type, verb and the resource status, it will behave differently.
-
#
-
# Using \Rails default responder, a POST request for creating an object could
-
# be written as:
-
#
-
# def create
-
# @user = User.new(params[:user])
-
# flash[:notice] = 'User was successfully created.' if @user.save
-
# respond_with(@user)
-
# end
-
#
-
# Which is exactly the same as:
-
#
-
# def create
-
# @user = User.new(params[:user])
-
#
-
# respond_to do |format|
-
# if @user.save
-
# flash[:notice] = 'User was successfully created.'
-
# format.html { redirect_to(@user) }
-
# format.xml { render :xml => @user, :status => :created, :location => @user }
-
# else
-
# format.html { render :action => "new" }
-
# format.xml { render :xml => @user.errors, :status => :unprocessable_entity }
-
# end
-
# end
-
# end
-
#
-
# The same happens for PUT and DELETE requests.
-
#
-
# === Nested resources
-
#
-
# You can supply nested resources as you do in <code>form_for</code> and <code>polymorphic_url</code>.
-
# Consider the project has many tasks example. The create action for
-
# TasksController would be like:
-
#
-
# def create
-
# @project = Project.find(params[:project_id])
-
# @task = @project.comments.build(params[:task])
-
# flash[:notice] = 'Task was successfully created.' if @task.save
-
# respond_with(@project, @task)
-
# end
-
#
-
# Giving several resources ensures that the responder will redirect to
-
# <code>project_task_url</code> instead of <code>task_url</code>.
-
#
-
# Namespaced and singleton resources require a symbol to be given, as in
-
# polymorphic urls. If a project has one manager which has many tasks, it
-
# should be invoked as:
-
#
-
# respond_with(@project, :manager, @task)
-
#
-
# Note that if you give an array, it will be treated as a collection,
-
# so the following is not equivalent:
-
#
-
# respond_with [@project, :manager, @task]
-
#
-
# === Custom options
-
#
-
# <code>respond_with</code> also allow you to pass options that are forwarded
-
# to the underlying render call. Those options are only applied success
-
# scenarios. For instance, you can do the following in the create method above:
-
#
-
# def create
-
# @project = Project.find(params[:project_id])
-
# @task = @project.comments.build(params[:task])
-
# flash[:notice] = 'Task was successfully created.' if @task.save
-
# respond_with(@project, @task, :status => 201)
-
# end
-
#
-
# This will return status 201 if the task was saved with success. If not,
-
# it will simply ignore the given options and return status 422 and the
-
# resource errors. To customize the failure scenario, you can pass a
-
# a block to <code>respond_with</code>:
-
#
-
# def create
-
# @project = Project.find(params[:project_id])
-
# @task = @project.comments.build(params[:task])
-
# respond_with(@project, @task, :status => 201) do |format|
-
# if @task.save
-
# flash[:notice] = 'Task was successfully created.'
-
# else
-
# format.html { render "some_special_template" }
-
# end
-
# end
-
# end
-
#
-
# Using <code>respond_with</code> with a block follows the same syntax as <code>respond_to</code>.
-
1
class Responder
-
1
attr_reader :controller, :request, :format, :resource, :resources, :options
-
-
1
ACTIONS_FOR_VERBS = {
-
:post => :new,
-
:put => :edit
-
}
-
-
1
def initialize(controller, resources, options={})
-
@controller = controller
-
@request = @controller.request
-
@format = @controller.formats.first
-
@resource = resources.last
-
@resources = resources
-
@options = options
-
@action = options.delete(:action)
-
@default_response = options.delete(:default_response)
-
end
-
-
1
delegate :head, :render, :redirect_to, :to => :controller
-
1
delegate :get?, :post?, :put?, :delete?, :to => :request
-
-
# Undefine :to_json and :to_yaml since it's defined on Object
-
1
undef_method(:to_json) if method_defined?(:to_json)
-
1
undef_method(:to_yaml) if method_defined?(:to_yaml)
-
-
# Initializes a new responder an invoke the proper format. If the format is
-
# not defined, call to_format.
-
#
-
1
def self.call(*args)
-
new(*args).respond
-
end
-
-
# Main entry point for responder responsible to dispatch to the proper format.
-
#
-
1
def respond
-
method = "to_#{format}"
-
respond_to?(method) ? send(method) : to_format
-
end
-
-
# HTML format does not render the resource, it always attempt to render a
-
# template.
-
#
-
1
def to_html
-
default_render
-
rescue ActionView::MissingTemplate => e
-
navigation_behavior(e)
-
end
-
-
# to_js simply tries to render a template. If no template is found, raises the error.
-
1
def to_js
-
default_render
-
end
-
-
# All other formats follow the procedure below. First we try to render a
-
# template, if the template is not available, we verify if the resource
-
# responds to :to_format and display it.
-
#
-
1
def to_format
-
if get? || !has_errors?
-
default_render
-
else
-
display_errors
-
end
-
rescue ActionView::MissingTemplate => e
-
api_behavior(e)
-
end
-
-
1
protected
-
-
# This is the common behavior for formats associated with browsing, like :html, :iphone and so forth.
-
1
def navigation_behavior(error)
-
if get?
-
raise error
-
elsif has_errors? && default_action
-
render :action => default_action
-
else
-
redirect_to navigation_location
-
end
-
end
-
-
# This is the common behavior for formats associated with APIs, such as :xml and :json.
-
1
def api_behavior(error)
-
raise error unless resourceful?
-
-
if get?
-
display resource
-
elsif post?
-
display resource, :status => :created, :location => api_location
-
elsif has_empty_resource_definition?
-
display empty_resource, :status => :ok
-
else
-
head :ok
-
end
-
end
-
-
# Checks whether the resource responds to the current format or not.
-
#
-
1
def resourceful?
-
resource.respond_to?("to_#{format}")
-
end
-
-
# Returns the resource location by retrieving it from the options or
-
# returning the resources array.
-
#
-
1
def resource_location
-
options[:location] || resources
-
end
-
1
alias :navigation_location :resource_location
-
1
alias :api_location :resource_location
-
-
# If a given response block was given, use it, otherwise call render on
-
# controller.
-
#
-
1
def default_render
-
@default_response.call(options)
-
end
-
-
# Display is just a shortcut to render a resource with the current format.
-
#
-
# display @user, :status => :ok
-
#
-
# For XML requests it's equivalent to:
-
#
-
# render :xml => @user, :status => :ok
-
#
-
# Options sent by the user are also used:
-
#
-
# respond_with(@user, :status => :created)
-
# display(@user, :status => :ok)
-
#
-
# Results in:
-
#
-
# render :xml => @user, :status => :created
-
#
-
1
def display(resource, given_options={})
-
controller.render given_options.merge!(options).merge!(format => resource)
-
end
-
-
1
def display_errors
-
controller.render format => resource.errors, :status => :unprocessable_entity
-
end
-
-
# Check whether the resource has errors.
-
#
-
1
def has_errors?
-
resource.respond_to?(:errors) && !resource.errors.empty?
-
end
-
-
# By default, render the <code>:edit</code> action for HTML requests with failure, unless
-
# the verb is POST.
-
#
-
1
def default_action
-
@action ||= ACTIONS_FOR_VERBS[request.request_method_symbol]
-
end
-
-
# Check whether resource needs a specific definition of empty resource to be valid
-
#
-
1
def has_empty_resource_definition?
-
respond_to?("empty_#{format}_resource")
-
end
-
-
# Delegate to proper empty resource method
-
#
-
1
def empty_resource
-
send("empty_#{format}_resource")
-
end
-
-
# Return a valid empty JSON resource
-
#
-
1
def empty_json_resource
-
"{}"
-
end
-
end
-
end
-
1
module ActionController #:nodoc:
-
1
module SessionManagement #:nodoc:
-
1
extend ActiveSupport::Concern
-
-
1
module ClassMethods
-
-
end
-
end
-
end
-
1
require 'active_support/core_ext/file/path'
-
1
require 'rack/chunked'
-
-
1
module ActionController #:nodoc:
-
# Allows views to be streamed back to the client as they are rendered.
-
#
-
# The default way Rails renders views is by first rendering the template
-
# and then the layout. The response is sent to the client after the whole
-
# template is rendered, all queries are made, and the layout is processed.
-
#
-
# Streaming inverts the rendering flow by rendering the layout first and
-
# streaming each part of the layout as they are processed. This allows the
-
# header of the HTML (which is usually in the layout) to be streamed back
-
# to client very quickly, allowing JavaScripts and stylesheets to be loaded
-
# earlier than usual.
-
#
-
# This approach was introduced in Rails 3.1 and is still improving. Several
-
# Rack middlewares may not work and you need to be careful when streaming.
-
# Those points are going to be addressed soon.
-
#
-
# In order to use streaming, you will need to use a Ruby version that
-
# supports fibers (fibers are supported since version 1.9.2 of the main
-
# Ruby implementation).
-
#
-
# == Examples
-
#
-
# Streaming can be added to a given template easily, all you need to do is
-
# to pass the :stream option.
-
#
-
# class PostsController
-
# def index
-
# @posts = Post.scoped
-
# render :stream => true
-
# end
-
# end
-
#
-
# == When to use streaming
-
#
-
# Streaming may be considered to be overkill for lightweight actions like
-
# +new+ or +edit+. The real benefit of streaming is on expensive actions
-
# that, for example, do a lot of queries on the database.
-
#
-
# In such actions, you want to delay queries execution as much as you can.
-
# For example, imagine the following +dashboard+ action:
-
#
-
# def dashboard
-
# @posts = Post.all
-
# @pages = Page.all
-
# @articles = Article.all
-
# end
-
#
-
# Most of the queries here are happening in the controller. In order to benefit
-
# from streaming you would want to rewrite it as:
-
#
-
# def dashboard
-
# # Allow lazy execution of the queries
-
# @posts = Post.scoped
-
# @pages = Page.scoped
-
# @articles = Article.scoped
-
# render :stream => true
-
# end
-
#
-
# Notice that :stream only works with templates. Rendering :json
-
# or :xml with :stream won't work.
-
#
-
# == Communication between layout and template
-
#
-
# When streaming, rendering happens top-down instead of inside-out.
-
# Rails starts with the layout, and the template is rendered later,
-
# when its +yield+ is reached.
-
#
-
# This means that, if your application currently relies on instance
-
# variables set in the template to be used in the layout, they won't
-
# work once you move to streaming. The proper way to communicate
-
# between layout and template, regardless of whether you use streaming
-
# or not, is by using +content_for+, +provide+ and +yield+.
-
#
-
# Take a simple example where the layout expects the template to tell
-
# which title to use:
-
#
-
# <html>
-
# <head><title><%= yield :title %></title></head>
-
# <body><%= yield %></body>
-
# </html>
-
#
-
# You would use +content_for+ in your template to specify the title:
-
#
-
# <%= content_for :title, "Main" %>
-
# Hello
-
#
-
# And the final result would be:
-
#
-
# <html>
-
# <head><title>Main</title></head>
-
# <body>Hello</body>
-
# </html>
-
#
-
# However, if +content_for+ is called several times, the final result
-
# would have all calls concatenated. For instance, if we have the following
-
# template:
-
#
-
# <%= content_for :title, "Main" %>
-
# Hello
-
# <%= content_for :title, " page" %>
-
#
-
# The final result would be:
-
#
-
# <html>
-
# <head><title>Main page</title></head>
-
# <body>Hello</body>
-
# </html>
-
#
-
# This means that, if you have <code>yield :title</code> in your layout
-
# and you want to use streaming, you would have to render the whole template
-
# (and eventually trigger all queries) before streaming the title and all
-
# assets, which kills the purpose of streaming. For this reason Rails 3.1
-
# introduces a new helper called +provide+ that does the same as +content_for+
-
# but tells the layout to stop searching for other entries and continue rendering.
-
#
-
# For instance, the template above using +provide+ would be:
-
#
-
# <%= provide :title, "Main" %>
-
# Hello
-
# <%= content_for :title, " page" %>
-
#
-
# Giving:
-
#
-
# <html>
-
# <head><title>Main</title></head>
-
# <body>Hello</body>
-
# </html>
-
#
-
# That said, when streaming, you need to properly check your templates
-
# and choose when to use +provide+ and +content_for+.
-
#
-
# == Headers, cookies, session and flash
-
#
-
# When streaming, the HTTP headers are sent to the client right before
-
# it renders the first line. This means that, modifying headers, cookies,
-
# session or flash after the template starts rendering will not propagate
-
# to the client.
-
#
-
# If you try to modify cookies, session or flash, an +ActionDispatch::ClosedError+
-
# will be raised, showing those objects are closed for modification.
-
#
-
# == Middlewares
-
#
-
# Middlewares that need to manipulate the body won't work with streaming.
-
# You should disable those middlewares whenever streaming in development
-
# or production. For instance, +Rack::Bug+ won't work when streaming as it
-
# needs to inject contents in the HTML body.
-
#
-
# Also +Rack::Cache+ won't work with streaming as it does not support
-
# streaming bodies yet. Whenever streaming Cache-Control is automatically
-
# set to "no-cache".
-
#
-
# == Errors
-
#
-
# When it comes to streaming, exceptions get a bit more complicated. This
-
# happens because part of the template was already rendered and streamed to
-
# the client, making it impossible to render a whole exception page.
-
#
-
# Currently, when an exception happens in development or production, Rails
-
# will automatically stream to the client:
-
#
-
# "><script type="text/javascript">window.location = "/500.html"</script></html>
-
#
-
# The first two characters (">) are required in case the exception happens
-
# while rendering attributes for a given tag. You can check the real cause
-
# for the exception in your logger.
-
#
-
# == Web server support
-
#
-
# Not all web servers support streaming out-of-the-box. You need to check
-
# the instructions for each of them.
-
#
-
# ==== Unicorn
-
#
-
# Unicorn supports streaming but it needs to be configured. For this, you
-
# need to create a config file as follow:
-
#
-
# # unicorn.config.rb
-
# listen 3000, :tcp_nopush => false
-
#
-
# And use it on initialization:
-
#
-
# unicorn_rails --config-file unicorn.config.rb
-
#
-
# You may also want to configure other parameters like <tt>:tcp_nodelay</tt>.
-
# Please check its documentation for more information: http://unicorn.bogomips.org/Unicorn/Configurator.html#method-i-listen
-
#
-
# If you are using Unicorn with Nginx, you may need to tweak Nginx.
-
# Streaming should work out of the box on Rainbows.
-
#
-
# ==== Passenger
-
#
-
# To be described.
-
#
-
1
module Streaming
-
1
extend ActiveSupport::Concern
-
-
1
include AbstractController::Rendering
-
1
attr_internal :stream
-
-
1
module ClassMethods
-
# Render streaming templates. It accepts :only, :except, :if and :unless as options
-
# to specify when to stream, as in ActionController filters.
-
1
def stream(options={})
-
ActiveSupport::Deprecation.warn "stream class method is deprecated. Please give the :stream option to render instead"
-
if defined?(Fiber)
-
before_filter :_stream_filter, options
-
else
-
raise "You cannot use streaming if Fiber is not available."
-
end
-
end
-
end
-
-
1
protected
-
-
# Mark following render calls as streaming.
-
1
def _stream_filter #:nodoc:
-
self.stream = true
-
end
-
-
# Consider the stream option when normalazing options.
-
1
def _normalize_options(options) #:nodoc:
-
super
-
options[:stream] = self.stream unless options.key?(:stream)
-
end
-
-
# Set proper cache control and transfer encoding when streaming
-
1
def _process_options(options) #:nodoc:
-
super
-
if options[:stream]
-
if env["HTTP_VERSION"] == "HTTP/1.0"
-
options.delete(:stream)
-
else
-
headers["Cache-Control"] ||= "no-cache"
-
headers["Transfer-Encoding"] = "chunked"
-
headers.delete("Content-Length")
-
end
-
end
-
end
-
-
# Call render_to_body if we are streaming instead of usual +render+.
-
1
def _render_template(options) #:nodoc:
-
if options.delete(:stream)
-
Rack::Chunked::Body.new view_renderer.render_body(view_context, options)
-
else
-
super
-
end
-
end
-
end
-
end
-
# Includes +url_for+ into the host class. The class has to provide a +RouteSet+ by implementing
-
# the <tt>_routes</tt> method. Otherwise, an exception will be raised.
-
#
-
# In addition to <tt>AbstractController::UrlFor</tt>, this module accesses the HTTP layer to define
-
# url options like the +host+. In order to do so, this module requires the host class
-
# to implement +env+ and +request+, which need to be a Rack-compatible.
-
#
-
# Example:
-
#
-
# class RootUrl
-
# include ActionController::UrlFor
-
# include Rails.application.routes.url_helpers
-
#
-
# delegate :env, :request, :to => :controller
-
#
-
# def initialize(controller)
-
# @controller = controller
-
# @url = root_path # named route from the application.
-
# end
-
# end
-
#
-
1
module ActionController
-
1
module UrlFor
-
1
extend ActiveSupport::Concern
-
-
1
include AbstractController::UrlFor
-
-
1
def url_options
-
@_url_options ||= super.reverse_merge(
-
:host => request.host,
-
:port => request.optional_port,
-
:protocol => request.protocol,
-
:_path_segments => request.symbolized_path_parameters
-
).freeze
-
-
if _routes.equal?(env["action_dispatch.routes"])
-
@_url_options.dup.tap do |options|
-
options[:script_name] = request.script_name.dup
-
options.freeze
-
end
-
else
-
@_url_options
-
end
-
end
-
-
end
-
end
-
1
require "rails"
-
1
require "action_controller"
-
1
require "action_dispatch/railtie"
-
1
require "action_view/railtie"
-
1
require "abstract_controller/railties/routes_helpers"
-
1
require "action_controller/railties/paths"
-
-
1
module ActionController
-
1
class Railtie < Rails::Railtie
-
1
config.action_controller = ActiveSupport::OrderedOptions.new
-
-
1
initializer "action_controller.logger" do
-
2
ActiveSupport.on_load(:action_controller) { self.logger ||= Rails.logger }
-
end
-
-
1
initializer "action_controller.initialize_framework_caches" do
-
2
ActiveSupport.on_load(:action_controller) { self.cache_store ||= RAILS_CACHE }
-
end
-
-
1
initializer "action_controller.set_configs" do |app|
-
1
paths = app.config.paths
-
1
options = app.config.action_controller
-
-
1
options.assets_dir ||= paths["public"].first
-
1
options.javascripts_dir ||= paths["public/javascripts"].first
-
1
options.stylesheets_dir ||= paths["public/stylesheets"].first
-
1
options.page_cache_directory ||= paths["public"].first
-
-
# make sure readers methods get compiled
-
1
options.asset_path ||= app.config.asset_path
-
1
options.asset_host ||= app.config.asset_host
-
-
1
ActiveSupport.on_load(:action_controller) do
-
1
include app.routes.mounted_helpers
-
1
extend ::AbstractController::Railties::RoutesHelpers.with(app.routes)
-
1
extend ::ActionController::Railties::Paths.with(app)
-
9
options.each { |k,v| send("#{k}=", v) }
-
end
-
end
-
-
1
initializer "action_controller.compile_config_methods" do
-
1
ActiveSupport.on_load(:action_controller) do
-
1
config.compile_methods! if config.respond_to?(:compile_methods!)
-
end
-
end
-
end
-
end
-
1
module ActionController
-
1
module Railties
-
1
module Paths
-
1
def self.with(app)
-
1
Module.new do
-
1
define_method(:inherited) do |klass|
-
4
super(klass)
-
-
8
if namespace = klass.parents.detect {|m| m.respond_to?(:_railtie) }
-
paths = namespace._railtie.paths["app/helpers"].existent
-
else
-
4
paths = app.config.helpers_paths
-
end
-
-
4
klass.helpers_path = paths
-
4
if klass.superclass == ActionController::Base && ActionController::Base.include_all_helpers
-
1
klass.helper :all
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module'
-
-
1
module ActionController
-
# The record identifier encapsulates a number of naming conventions for dealing with records, like Active Records or
-
# Active Resources or pretty much any other model type that has an id. These patterns are then used to try elevate
-
# the view actions to a higher logical level. Example:
-
#
-
# # routes
-
# resources :posts
-
#
-
# # view
-
# <%= div_for(post) do %> <div id="post_45" class="post">
-
# <%= post.body %> What a wonderful world!
-
# <% end %> </div>
-
#
-
# # controller
-
# def destroy
-
# post = Post.find(params[:id])
-
# post.destroy
-
#
-
# redirect_to(post) # Calls polymorphic_url(post) which in turn calls post_url(post)
-
# end
-
#
-
# As the example above shows, you can stop caring to a large extent what the actual id of the post is.
-
# You just know that one is being assigned and that the subsequent calls in redirect_to expect that
-
# same naming convention and allows you to write less code if you follow it.
-
1
module RecordIdentifier
-
1
extend self
-
-
1
JOIN = '_'.freeze
-
1
NEW = 'new'.freeze
-
-
# The DOM class convention is to use the singular form of an object or class. Examples:
-
#
-
# dom_class(post) # => "post"
-
# dom_class(Person) # => "person"
-
#
-
# If you need to address multiple instances of the same class in the same view, you can prefix the dom_class:
-
#
-
# dom_class(post, :edit) # => "edit_post"
-
# dom_class(Person, :edit) # => "edit_person"
-
1
def dom_class(record_or_class, prefix = nil)
-
singular = ActiveModel::Naming.singular(record_or_class)
-
prefix ? "#{prefix}#{JOIN}#{singular}" : singular
-
end
-
-
# The DOM id convention is to use the singular form of an object or class with the id following an underscore.
-
# If no id is found, prefix with "new_" instead. Examples:
-
#
-
# dom_id(Post.find(45)) # => "post_45"
-
# dom_id(Post.new) # => "new_post"
-
#
-
# If you need to address multiple instances of the same class in the same view, you can prefix the dom_id:
-
#
-
# dom_id(Post.find(45), :edit) # => "edit_post_45"
-
1
def dom_id(record, prefix = nil)
-
if record_id = record_key_for_dom_id(record)
-
"#{dom_class(record, prefix)}#{JOIN}#{record_id}"
-
else
-
dom_class(record, prefix || NEW)
-
end
-
end
-
-
1
protected
-
-
# Returns a string representation of the key attribute(s) that is suitable for use in an HTML DOM id.
-
# This can be overwritten to customize the default generated string representation if desired.
-
# If you need to read back a key from a dom_id in order to query for the underlying database record,
-
# you should write a helper like 'person_record_from_dom_id' that will extract the key either based
-
# on the default implementation (which just joins all key attributes with '-') or on your own
-
# overwritten version of the method. By default, this implementation passes the key string through a
-
# method that replaces all characters that are invalid inside DOM ids, with valid ones. You need to
-
# make sure yourself that your dom ids are valid, in case you overwrite this method.
-
1
def record_key_for_dom_id(record)
-
record = record.to_model if record.respond_to?(:to_model)
-
key = record.to_key
-
key ? sanitize_dom_id(key.join('_')) : key
-
end
-
-
# Replaces characters that are invalid in HTML DOM ids with valid ones.
-
1
def sanitize_dom_id(candidate_id)
-
candidate_id # TODO implement conversion to valid DOM id values
-
end
-
end
-
end
-
1
require 'rack/session/abstract/id'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/object/to_query'
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/module/anonymous'
-
-
1
module ActionController
-
1
module TemplateAssertions
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
2
setup :setup_subscriptions
-
2
teardown :teardown_subscriptions
-
end
-
-
1
def setup_subscriptions
-
@partials = Hash.new(0)
-
@templates = Hash.new(0)
-
@layouts = Hash.new(0)
-
-
ActiveSupport::Notifications.subscribe("render_template.action_view") do |name, start, finish, id, payload|
-
path = payload[:layout]
-
@layouts[path] += 1
-
end
-
-
ActiveSupport::Notifications.subscribe("!render_template.action_view") do |name, start, finish, id, payload|
-
path = payload[:virtual_path]
-
next unless path
-
partial = path =~ /^.*\/_[^\/]*$/
-
if partial
-
@partials[path] += 1
-
@partials[path.split("/").last] += 1
-
@templates[path] += 1
-
else
-
@templates[path] += 1
-
end
-
end
-
end
-
-
1
def teardown_subscriptions
-
ActiveSupport::Notifications.unsubscribe("render_template.action_view")
-
ActiveSupport::Notifications.unsubscribe("!render_template.action_view")
-
end
-
-
1
def process(*args)
-
@partials = Hash.new(0)
-
@templates = Hash.new(0)
-
@layouts = Hash.new(0)
-
super
-
end
-
-
# Asserts that the request was rendered with the appropriate template file or partials.
-
#
-
# ==== Examples
-
#
-
# # assert that the "new" view template was rendered
-
# assert_template "new"
-
#
-
# # assert that the "_customer" partial was rendered twice
-
# assert_template :partial => '_customer', :count => 2
-
#
-
# # assert that no partials were rendered
-
# assert_template :partial => false
-
#
-
# In a view test case, you can also assert that specific locals are passed
-
# to partials:
-
#
-
# # assert that the "_customer" partial was rendered with a specific object
-
# assert_template :partial => '_customer', :locals => { :customer => @customer }
-
#
-
1
def assert_template(options = {}, message = nil)
-
validate_request!
-
-
case options
-
when NilClass, String, Symbol
-
options = options.to_s if Symbol === options
-
rendered = @templates
-
msg = build_message(message,
-
"expecting <?> but rendering with <?>",
-
options, rendered.keys.join(', '))
-
assert_block(msg) do
-
if options.nil?
-
@templates.blank?
-
else
-
rendered.any? { |t,num| t.match(options) }
-
end
-
end
-
when Hash
-
if expected_partial = options[:partial]
-
if expected_locals = options[:locals]
-
actual_locals = @locals[expected_partial.to_s.sub(/^_/,'')]
-
expected_locals.each_pair do |k,v|
-
assert_equal(v, actual_locals[k])
-
end
-
elsif expected_count = options[:count]
-
actual_count = @partials[expected_partial]
-
msg = build_message(message,
-
"expecting ? to be rendered ? time(s) but rendered ? time(s)",
-
expected_partial, expected_count, actual_count)
-
assert(actual_count == expected_count.to_i, msg)
-
elsif options.key?(:layout)
-
msg = build_message(message,
-
"expecting layout <?> but action rendered <?>",
-
expected_layout, @layouts.keys)
-
-
case layout = options[:layout]
-
when String
-
assert(@layouts.include?(expected_layout), msg)
-
when Regexp
-
assert(@layouts.any? {|l| l =~ layout }, msg)
-
when nil
-
assert(@layouts.empty?, msg)
-
end
-
else
-
msg = build_message(message,
-
"expecting partial <?> but action rendered <?>",
-
options[:partial], @partials.keys)
-
assert(@partials.include?(expected_partial), msg)
-
end
-
else
-
assert @partials.empty?,
-
"Expected no partials to be rendered"
-
end
-
end
-
end
-
end
-
-
1
class TestRequest < ActionDispatch::TestRequest #:nodoc:
-
1
def initialize(env = {})
-
super
-
-
self.session = TestSession.new
-
self.session_options = TestSession::DEFAULT_OPTIONS.merge(:id => SecureRandom.hex(16))
-
end
-
-
1
class Result < ::Array #:nodoc:
-
1
def to_s() join '/' end
-
1
def self.new_escaped(strings)
-
new strings.collect {|str| uri_parser.unescape str}
-
end
-
end
-
-
1
def assign_parameters(routes, controller_path, action, parameters = {})
-
parameters = parameters.symbolize_keys.merge(:controller => controller_path, :action => action)
-
extra_keys = routes.extra_keys(parameters)
-
non_path_parameters = get? ? query_parameters : request_parameters
-
parameters.each do |key, value|
-
if value.is_a? Fixnum
-
value = value.to_s
-
elsif value.is_a? Array
-
value = Result.new(value.map { |v| v.is_a?(String) ? v.dup : v })
-
elsif value.is_a? String
-
value = value.dup
-
end
-
-
if extra_keys.include?(key.to_sym)
-
non_path_parameters[key] = value
-
else
-
path_parameters[key.to_s] = value
-
end
-
end
-
-
# Clear the combined params hash in case it was already referenced.
-
@env.delete("action_dispatch.request.parameters")
-
-
params = self.request_parameters.dup
-
%w(controller action only_path).each do |k|
-
params.delete(k)
-
params.delete(k.to_sym)
-
end
-
data = params.to_query
-
-
@env['CONTENT_LENGTH'] = data.length.to_s
-
@env['rack.input'] = StringIO.new(data)
-
end
-
-
1
def recycle!
-
write_cookies!
-
@env.delete('HTTP_COOKIE') if @cookies.blank?
-
@env.delete('action_dispatch.cookies')
-
@cookies = nil
-
@formats = nil
-
@env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
-
@env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
-
@symbolized_path_params = nil
-
@method = @request_method = nil
-
@fullpath = @ip = @remote_ip = @protocol = nil
-
@env['action_dispatch.request.query_parameters'] = {}
-
end
-
end
-
-
1
class TestResponse < ActionDispatch::TestResponse
-
1
def recycle!
-
@status = 200
-
@header = {}
-
@writer = lambda { |x| @body << x }
-
@block = nil
-
@length = 0
-
@body = []
-
@charset = @content_type = nil
-
@request = @template = nil
-
end
-
end
-
-
1
class TestSession < Rack::Session::Abstract::SessionHash #:nodoc:
-
1
DEFAULT_OPTIONS = Rack::Session::Abstract::ID::DEFAULT_OPTIONS
-
-
1
def initialize(session = {})
-
@env, @by = nil, nil
-
replace(session.stringify_keys)
-
@loaded = true
-
end
-
-
1
def exists?
-
true
-
end
-
end
-
-
# Superclass for ActionController functional tests. Functional tests allow you to
-
# test a single controller action per test method. This should not be confused with
-
# integration tests (see ActionDispatch::IntegrationTest), which are more like
-
# "stories" that can involve multiple controllers and multiple actions (i.e. multiple
-
# different HTTP requests).
-
#
-
# == Basic example
-
#
-
# Functional tests are written as follows:
-
# 1. First, one uses the +get+, +post+, +put+, +delete+ or +head+ method to simulate
-
# an HTTP request.
-
# 2. Then, one asserts whether the current state is as expected. "State" can be anything:
-
# the controller's HTTP response, the database contents, etc.
-
#
-
# For example:
-
#
-
# class BooksControllerTest < ActionController::TestCase
-
# def test_create
-
# # Simulate a POST response with the given HTTP parameters.
-
# post(:create, :book => { :title => "Love Hina" })
-
#
-
# # Assert that the controller tried to redirect us to
-
# # the created book's URI.
-
# assert_response :found
-
#
-
# # Assert that the controller really put the book in the database.
-
# assert_not_nil Book.find_by_title("Love Hina")
-
# end
-
# end
-
#
-
# == Special instance variables
-
#
-
# ActionController::TestCase will also automatically provide the following instance
-
# variables for use in the tests:
-
#
-
# <b>@controller</b>::
-
# The controller instance that will be tested.
-
# <b>@request</b>::
-
# An ActionController::TestRequest, representing the current HTTP
-
# request. You can modify this object before sending the HTTP request. For example,
-
# you might want to set some session properties before sending a GET request.
-
# <b>@response</b>::
-
# An ActionController::TestResponse object, representing the response
-
# of the last HTTP response. In the above example, <tt>@response</tt> becomes valid
-
# after calling +post+. If the various assert methods are not sufficient, then you
-
# may use this object to inspect the HTTP response in detail.
-
#
-
# (Earlier versions of \Rails required each functional test to subclass
-
# Test::Unit::TestCase and define @controller, @request, @response in +setup+.)
-
#
-
# == Controller is automatically inferred
-
#
-
# ActionController::TestCase will automatically infer the controller under test
-
# from the test class name. If the controller cannot be inferred from the test
-
# class name, you can explicitly set it with +tests+.
-
#
-
# class SpecialEdgeCaseWidgetsControllerTest < ActionController::TestCase
-
# tests WidgetController
-
# end
-
#
-
# == \Testing controller internals
-
#
-
# In addition to these specific assertions, you also have easy access to various collections that the regular test/unit assertions
-
# can be used against. These collections are:
-
#
-
# * assigns: Instance variables assigned in the action that are available for the view.
-
# * session: Objects being saved in the session.
-
# * flash: The flash objects currently in the session.
-
# * cookies: \Cookies being sent to the user on this request.
-
#
-
# These collections can be used just like any other hash:
-
#
-
# assert_not_nil assigns(:person) # makes sure that a @person instance variable was set
-
# assert_equal "Dave", cookies[:name] # makes sure that a cookie called :name was set as "Dave"
-
# assert flash.empty? # makes sure that there's nothing in the flash
-
#
-
# For historic reasons, the assigns hash uses string-based keys. So assigns[:person] won't work, but assigns["person"] will. To
-
# appease our yearning for symbols, though, an alternative accessor has been devised using a method call instead of index referencing.
-
# So assigns(:person) will work just like assigns["person"], but again, assigns[:person] will not work.
-
#
-
# On top of the collections, you have the complete url that a given action redirected to available in redirect_to_url.
-
#
-
# For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another
-
# action call which can then be asserted against.
-
#
-
# == Manipulating the request collections
-
#
-
# The collections described above link to the response, so you can test if what the actions were expected to do happened. But
-
# sometimes you also want to manipulate these collections in the incoming request. This is really only relevant for sessions
-
# and cookies, though. For sessions, you just do:
-
#
-
# @request.session[:key] = "value"
-
# @request.cookies[:key] = "value"
-
#
-
# To clear the cookies for a test just clear the request's cookies hash:
-
#
-
# @request.cookies.clear
-
#
-
# == \Testing named routes
-
#
-
# If you're using named routes, they can be easily tested using the original named routes' methods straight in the test case.
-
# Example:
-
#
-
# assert_redirected_to page_url(:title => 'foo')
-
1
class TestCase < ActiveSupport::TestCase
-
1
module Behavior
-
1
extend ActiveSupport::Concern
-
1
include ActionDispatch::TestProcess
-
-
1
attr_reader :response, :request
-
-
1
module ClassMethods
-
-
# Sets the controller class name. Useful if the name can't be inferred from test class.
-
# Expects +controller_class+ as a constant. Example: <tt>tests WidgetController</tt>.
-
1
def tests(controller_class)
-
self.controller_class = controller_class
-
end
-
-
1
def controller_class=(new_class)
-
prepare_controller_class(new_class) if new_class
-
self._controller_class = new_class
-
end
-
-
1
def controller_class
-
if current_controller_class = self._controller_class
-
current_controller_class
-
else
-
self.controller_class = determine_default_controller_class(name)
-
end
-
end
-
-
1
def determine_default_controller_class(name)
-
name.sub(/Test$/, '').constantize
-
rescue NameError
-
nil
-
end
-
-
1
def prepare_controller_class(new_class)
-
new_class.send :include, ActionController::TestCase::RaiseActionExceptions
-
end
-
-
end
-
-
# Executes a request simulating GET HTTP method and set/volley the response
-
1
def get(action, parameters = nil, session = nil, flash = nil)
-
process(action, parameters, session, flash, "GET")
-
end
-
-
# Executes a request simulating POST HTTP method and set/volley the response
-
1
def post(action, parameters = nil, session = nil, flash = nil)
-
process(action, parameters, session, flash, "POST")
-
end
-
-
# Executes a request simulating PUT HTTP method and set/volley the response
-
1
def put(action, parameters = nil, session = nil, flash = nil)
-
process(action, parameters, session, flash, "PUT")
-
end
-
-
# Executes a request simulating DELETE HTTP method and set/volley the response
-
1
def delete(action, parameters = nil, session = nil, flash = nil)
-
process(action, parameters, session, flash, "DELETE")
-
end
-
-
# Executes a request simulating HEAD HTTP method and set/volley the response
-
1
def head(action, parameters = nil, session = nil, flash = nil)
-
process(action, parameters, session, flash, "HEAD")
-
end
-
-
1
def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
-
@request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
-
@request.env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
-
__send__(request_method, action, parameters, session, flash).tap do
-
@request.env.delete 'HTTP_X_REQUESTED_WITH'
-
@request.env.delete 'HTTP_ACCEPT'
-
end
-
end
-
1
alias xhr :xml_http_request
-
-
1
def paramify_values(hash_or_array_or_value)
-
case hash_or_array_or_value
-
when Hash
-
Hash[hash_or_array_or_value.map{|key, value| [key, paramify_values(value)] }]
-
when Array
-
hash_or_array_or_value.map {|i| paramify_values(i)}
-
when Rack::Test::UploadedFile
-
hash_or_array_or_value
-
else
-
hash_or_array_or_value.to_param
-
end
-
end
-
-
1
def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
-
# Ensure that numbers and symbols passed as params are converted to
-
# proper params, as is the case when engaging rack.
-
parameters = paramify_values(parameters)
-
-
# Sanity check for required instance variables so we can give an
-
# understandable error message.
-
%w(@routes @controller @request @response).each do |iv_name|
-
if !(instance_variable_names.include?(iv_name) || instance_variable_names.include?(iv_name.to_sym)) || instance_variable_get(iv_name).nil?
-
raise "#{iv_name} is nil: make sure you set it in your test's setup method."
-
end
-
end
-
-
@request.recycle!
-
@response.recycle!
-
@controller.response_body = nil
-
@controller.formats = nil
-
@controller.params = nil
-
-
@html_document = nil
-
@request.env['REQUEST_METHOD'] = http_method
-
-
parameters ||= {}
-
controller_class_name = @controller.class.anonymous? ?
-
"anonymous_controller" :
-
@controller.class.name.underscore.sub(/_controller$/, '')
-
-
@request.assign_parameters(@routes, controller_class_name, action.to_s, parameters)
-
-
@request.session = ActionController::TestSession.new(session) if session
-
@request.session["flash"] = @request.flash.update(flash || {})
-
@request.session["flash"].sweep
-
-
@controller.request = @request
-
@controller.params.merge!(parameters)
-
build_request_uri(action, parameters)
-
@controller.class.class_eval { include Testing }
-
@controller.recycle!
-
@controller.process_with_new_base_test(@request, @response)
-
@assigns = @controller.respond_to?(:view_assigns) ? @controller.view_assigns : {}
-
@request.session.delete('flash') if @request.session['flash'].blank?
-
@request.cookies.merge!(@response.cookies)
-
@response
-
end
-
-
1
def setup_controller_request_and_response
-
@request = TestRequest.new
-
@response = TestResponse.new
-
-
if klass = self.class.controller_class
-
@controller ||= klass.new rescue nil
-
end
-
-
@request.env.delete('PATH_INFO')
-
-
if defined?(@controller) && @controller
-
@controller.request = @request
-
@controller.params = {}
-
end
-
end
-
-
# Cause the action to be rescued according to the regular rules for rescue_action when the visitor is not local
-
1
def rescue_action_in_public!
-
@request.remote_addr = '208.77.188.166' # example.com
-
end
-
-
1
included do
-
1
include ActionController::TemplateAssertions
-
1
include ActionDispatch::Assertions
-
1
class_attribute :_controller_class
-
1
setup :setup_controller_request_and_response
-
end
-
-
1
private
-
-
1
def build_request_uri(action, parameters)
-
unless @request.env["PATH_INFO"]
-
options = @controller.respond_to?(:url_options) ? @controller.__send__(:url_options).merge(parameters) : parameters
-
options.update(
-
:only_path => true,
-
:action => action,
-
:relative_url_root => nil,
-
:_path_segments => @request.symbolized_path_parameters)
-
-
url, query_string = @routes.url_for(options).split("?", 2)
-
-
@request.env["SCRIPT_NAME"] = @controller.config.relative_url_root
-
@request.env["PATH_INFO"] = url
-
@request.env["QUERY_STRING"] = query_string || ""
-
end
-
end
-
end
-
-
# When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline
-
# (skipping the regular exception handling from rescue_action). If the request.remote_addr is anything else, the regular
-
# rescue_action process takes place. This means you can test your rescue_action code by setting remote_addr to something else
-
# than 0.0.0.0.
-
#
-
# The exception is stored in the exception accessor for further inspection.
-
1
module RaiseActionExceptions
-
1
def self.included(base)
-
unless base.method_defined?(:exception) && base.method_defined?(:exception=)
-
base.class_eval do
-
attr_accessor :exception
-
protected :exception, :exception=
-
end
-
end
-
end
-
-
1
protected
-
1
def rescue_action_without_handler(e)
-
self.exception = e
-
-
if request.remote_addr == "0.0.0.0"
-
raise(e)
-
else
-
super(e)
-
end
-
end
-
end
-
-
1
include Behavior
-
end
-
end
-
1
$LOAD_PATH << "#{File.dirname(__FILE__)}/html-scanner"
-
-
1
module HTML
-
1
extend ActiveSupport::Autoload
-
-
1
eager_autoload do
-
1
autoload :CDATA, 'html/node'
-
1
autoload :Document, 'html/document'
-
1
autoload :FullSanitizer, 'html/sanitizer'
-
1
autoload :LinkSanitizer, 'html/sanitizer'
-
1
autoload :Node, 'html/node'
-
1
autoload :Sanitizer, 'html/sanitizer'
-
1
autoload :Selector, 'html/selector'
-
1
autoload :Tag, 'html/node'
-
1
autoload :Text, 'html/node'
-
1
autoload :Tokenizer, 'html/tokenizer'
-
1
autoload :Version, 'html/version'
-
1
autoload :WhiteListSanitizer, 'html/sanitizer'
-
end
-
end
-
1
require 'active_support/core_ext/object/blank'
-
-
1
module ActionDispatch
-
1
module Http
-
1
module Cache
-
1
module Request
-
1
def if_modified_since
-
if since = env['HTTP_IF_MODIFIED_SINCE']
-
Time.rfc2822(since) rescue nil
-
end
-
end
-
-
1
def if_none_match
-
env['HTTP_IF_NONE_MATCH']
-
end
-
-
1
def not_modified?(modified_at)
-
if_modified_since && modified_at && if_modified_since >= modified_at
-
end
-
-
1
def etag_matches?(etag)
-
if_none_match && if_none_match == etag
-
end
-
-
# Check response freshness (Last-Modified and ETag) against request
-
# If-Modified-Since and If-None-Match conditions. If both headers are
-
# supplied, both must match, or the request is not considered fresh.
-
1
def fresh?(response)
-
last_modified = if_modified_since
-
etag = if_none_match
-
-
return false unless last_modified || etag
-
-
success = true
-
success &&= not_modified?(response.last_modified) if last_modified
-
success &&= etag_matches?(response.etag) if etag
-
success
-
end
-
end
-
-
1
module Response
-
1
attr_reader :cache_control, :etag
-
1
alias :etag? :etag
-
-
1
def last_modified
-
if last = headers['Last-Modified']
-
Time.httpdate(last)
-
end
-
end
-
-
1
def last_modified?
-
headers.include?('Last-Modified')
-
end
-
-
1
def last_modified=(utc_time)
-
headers['Last-Modified'] = utc_time.httpdate
-
end
-
-
1
def etag=(etag)
-
key = ActiveSupport::Cache.expand_cache_key(etag)
-
@etag = self["ETag"] = %("#{Digest::MD5.hexdigest(key)}")
-
end
-
-
1
private
-
-
1
def prepare_cache_control!
-
@cache_control = {}
-
@etag = self["ETag"]
-
-
if cache_control = self["Cache-Control"]
-
cache_control.split(/,\s*/).each do |segment|
-
first, last = segment.split("=")
-
@cache_control[first.to_sym] = last || true
-
end
-
end
-
end
-
-
1
def handle_conditional_get!
-
if etag? || last_modified? || !@cache_control.empty?
-
set_conditional_cache_control!
-
end
-
end
-
-
1
DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate"
-
-
1
def set_conditional_cache_control!
-
return if self["Cache-Control"].present?
-
-
control = @cache_control
-
-
if control.empty?
-
headers["Cache-Control"] = DEFAULT_CACHE_CONTROL
-
elsif control[:no_cache]
-
headers["Cache-Control"] = "no-cache"
-
else
-
extras = control[:extras]
-
max_age = control[:max_age]
-
-
options = []
-
options << "max-age=#{max_age.to_i}" if max_age
-
options << (control[:public] ? "public" : "private")
-
options << "must-revalidate" if control[:must_revalidate]
-
options.concat(extras) if extras
-
-
headers["Cache-Control"] = options.join(", ")
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/core_ext/object/duplicable'
-
-
1
module ActionDispatch
-
1
module Http
-
# Allows you to specify sensitive parameters which will be replaced from
-
# the request log by looking in the query string of the request and all
-
# subhashes of the params hash to filter. If a block is given, each key and
-
# value of the params hash and all subhashes is passed to it, the value
-
# or key can be replaced using String#replace or similar method.
-
#
-
# Examples:
-
#
-
# env["action_dispatch.parameter_filter"] = [:password]
-
# => replaces the value to all keys matching /password/i with "[FILTERED]"
-
#
-
# env["action_dispatch.parameter_filter"] = [:foo, "bar"]
-
# => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
-
#
-
# env["action_dispatch.parameter_filter"] = lambda do |k,v|
-
# v.reverse! if k =~ /secret/i
-
# end
-
# => reverses the value to all keys matching /secret/i
-
#
-
1
module FilterParameters
-
1
extend ActiveSupport::Concern
-
-
1
@@parameter_filter_for = {}
-
-
# Return a hash of parameters with all sensitive data replaced.
-
1
def filtered_parameters
-
@filtered_parameters ||= parameter_filter.filter(parameters)
-
end
-
-
# Return a hash of request.env with all sensitive data replaced.
-
1
def filtered_env
-
@filtered_env ||= env_filter.filter(@env)
-
end
-
-
# Reconstructed a path with all sensitive GET parameters replaced.
-
1
def filtered_path
-
@filtered_path ||= query_string.empty? ? path : "#{path}?#{filtered_query_string}"
-
end
-
-
1
protected
-
-
1
def parameter_filter
-
parameter_filter_for(@env["action_dispatch.parameter_filter"])
-
end
-
-
1
def env_filter
-
parameter_filter_for(Array.wrap(@env["action_dispatch.parameter_filter"]) << /RAW_POST_DATA/)
-
end
-
-
1
def parameter_filter_for(filters)
-
@@parameter_filter_for[filters] ||= ParameterFilter.new(filters)
-
end
-
-
1
KV_RE = '[^&;=]+'
-
1
PAIR_RE = %r{(#{KV_RE})=(#{KV_RE})}
-
1
def filtered_query_string
-
query_string.gsub(PAIR_RE) do |_|
-
parameter_filter.filter([[$1, $2]]).first.join("=")
-
end
-
end
-
-
end
-
end
-
end
-
1
require 'active_support/memoizable'
-
-
1
module ActionDispatch
-
1
module Http
-
1
class Headers < ::Hash
-
1
extend ActiveSupport::Memoizable
-
-
1
def initialize(*args)
-
if args.size == 1 && args[0].is_a?(Hash)
-
super()
-
update(args[0])
-
else
-
super
-
end
-
end
-
-
1
def [](header_name)
-
if include?(header_name)
-
super
-
else
-
super(env_name(header_name))
-
end
-
end
-
-
1
private
-
# Converts a HTTP header name to an environment variable name.
-
1
def env_name(header_name)
-
"HTTP_#{header_name.upcase.gsub(/-/, '_')}"
-
end
-
1
memoize :env_name
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
module Http
-
1
module MimeNegotiation
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
mattr_accessor :ignore_accept_header
-
1
self.ignore_accept_header = false
-
end
-
-
# The MIME type of the HTTP request, such as Mime::XML.
-
#
-
# For backward compatibility, the post \format is extracted from the
-
# X-Post-Data-Format HTTP header if present.
-
1
def content_mime_type
-
@env["action_dispatch.request.content_type"] ||= begin
-
if @env['CONTENT_TYPE'] =~ /^([^,\;]*)/
-
Mime::Type.lookup($1.strip.downcase)
-
else
-
nil
-
end
-
end
-
end
-
-
1
def content_type
-
content_mime_type && content_mime_type.to_s
-
end
-
-
# Returns the accepted MIME type for the request.
-
1
def accepts
-
@env["action_dispatch.request.accepts"] ||= begin
-
header = @env['HTTP_ACCEPT'].to_s.strip
-
-
if header.empty?
-
[content_mime_type]
-
else
-
Mime::Type.parse(header)
-
end
-
end
-
end
-
-
# Returns the MIME type for the \format used in the request.
-
#
-
# GET /posts/5.xml | request.format => Mime::XML
-
# GET /posts/5.xhtml | request.format => Mime::HTML
-
# GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first
-
#
-
1
def format(view_path = [])
-
formats.first
-
end
-
-
1
def formats
-
@env["action_dispatch.request.formats"] ||=
-
if parameters[:format]
-
Array(Mime[parameters[:format]])
-
elsif use_accept_header && valid_accept_header
-
accepts
-
elsif xhr?
-
[Mime::JS]
-
else
-
[Mime::HTML]
-
end
-
end
-
-
# Sets the \format by string extension, which can be used to force custom formats
-
# that are not controlled by the extension.
-
#
-
# class ApplicationController < ActionController::Base
-
# before_filter :adjust_format_for_iphone
-
#
-
# private
-
# def adjust_format_for_iphone
-
# request.format = :iphone if request.env["HTTP_USER_AGENT"][/iPhone/]
-
# end
-
# end
-
1
def format=(extension)
-
parameters[:format] = extension.to_s
-
@env["action_dispatch.request.formats"] = [Mime::Type.lookup_by_extension(parameters[:format])]
-
end
-
-
# Receives an array of mimes and return the first user sent mime that
-
# matches the order array.
-
#
-
1
def negotiate_mime(order)
-
formats.each do |priority|
-
if priority == Mime::ALL
-
return order.first
-
elsif order.include?(priority)
-
return priority
-
end
-
end
-
-
order.include?(Mime::ALL) ? formats.first : nil
-
end
-
-
1
protected
-
-
1
BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/
-
-
1
def valid_accept_header
-
xhr? || (accept && accept !~ BROWSER_LIKE_ACCEPTS)
-
end
-
-
1
def use_accept_header
-
!self.class.ignore_accept_header
-
end
-
end
-
end
-
end
-
1
require 'set'
-
1
require 'active_support/core_ext/class/attribute_accessors'
-
1
require 'active_support/core_ext/object/blank'
-
-
1
module Mime
-
1
class Mimes < Array
-
1
def symbols
-
@symbols ||= map {|m| m.to_sym }
-
end
-
-
1
%w(<< concat shift unshift push pop []= clear compact! collect!
-
delete delete_at delete_if flatten! map! insert reject! reverse!
-
replace slice! sort! uniq!).each do |method|
-
22
module_eval <<-CODE, __FILE__, __LINE__ + 1
-
def #{method}(*)
-
@symbols = nil
-
super
-
end
-
CODE
-
end
-
end
-
-
1
SET = Mimes.new
-
1
EXTENSION_LOOKUP = {}
-
1
LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
-
-
1
def self.[](type)
-
return type if type.is_a?(Type)
-
Type.lookup_by_extension(type.to_s)
-
end
-
-
# Encapsulates the notion of a mime type. Can be used at render time, for example, with:
-
#
-
# class PostsController < ActionController::Base
-
# def show
-
# @post = Post.find(params[:id])
-
#
-
# respond_to do |format|
-
# format.html
-
# format.ics { render :text => post.to_ics, :mime_type => Mime::Type["text/calendar"] }
-
# format.xml { render :xml => @people.to_xml }
-
# end
-
# end
-
# end
-
1
class Type
-
1
@@html_types = Set.new [:html, :all]
-
1
cattr_reader :html_types
-
-
# These are the content types which browsers can generate without using ajax, flash, etc
-
# i.e. following a link, getting an image or posting a form. CSRF protection
-
# only needs to protect against these types.
-
1
@@browser_generated_types = Set.new [:html, :url_encoded_form, :multipart_form, :text]
-
1
cattr_reader :browser_generated_types
-
1
attr_reader :symbol
-
-
# A simple helper class used in parsing the accept header
-
1
class AcceptItem #:nodoc:
-
1
attr_accessor :order, :name, :q
-
-
1
def initialize(order, name, q=nil)
-
@order = order
-
@name = name.strip
-
q ||= 0.0 if @name == Mime::ALL # default wildcard match to end of list
-
@q = ((q || 1.0).to_f * 100).to_i
-
end
-
-
1
def to_s
-
@name
-
end
-
-
1
def <=>(item)
-
result = item.q <=> q
-
result = order <=> item.order if result == 0
-
result
-
end
-
-
1
def ==(item)
-
name == (item.respond_to?(:name) ? item.name : item)
-
end
-
end
-
-
1
class << self
-
-
1
TRAILING_STAR_REGEXP = /(text|application)\/\*/
-
-
1
def lookup(string)
-
LOOKUP[string]
-
end
-
-
1
def lookup_by_extension(extension)
-
EXTENSION_LOOKUP[extension.to_s]
-
end
-
-
# Registers an alias that's not used on mime type lookup, but can be referenced directly. Especially useful for
-
# rendering different HTML versions depending on the user agent, like an iPhone.
-
1
def register_alias(string, symbol, extension_synonyms = [])
-
register(string, symbol, [], extension_synonyms, true)
-
end
-
-
1
def register(string, symbol, mime_type_synonyms = [], extension_synonyms = [], skip_lookup = false)
-
13
Mime.const_set(symbol.to_s.upcase, Type.new(string, symbol, mime_type_synonyms))
-
-
13
SET << Mime.const_get(symbol.to_s.upcase)
-
-
34
([string] + mime_type_synonyms).each { |str| LOOKUP[str] = SET.last } unless skip_lookup
-
28
([symbol.to_s] + extension_synonyms).each { |ext| EXTENSION_LOOKUP[ext] = SET.last }
-
end
-
-
1
def parse(accept_header)
-
if accept_header !~ /,/
-
if accept_header =~ TRAILING_STAR_REGEXP
-
parse_data_with_trailing_star($1)
-
else
-
[Mime::Type.lookup(accept_header)]
-
end
-
else
-
# keep track of creation order to keep the subsequent sort stable
-
list, index = [], 0
-
accept_header.split(/,/).each do |header|
-
params, q = header.split(/;\s*q=/)
-
if params.present?
-
params.strip!
-
-
if params =~ TRAILING_STAR_REGEXP
-
parse_data_with_trailing_star($1).each do |m|
-
list << AcceptItem.new(index, m.to_s, q)
-
index += 1
-
end
-
else
-
list << AcceptItem.new(index, params, q)
-
index += 1
-
end
-
end
-
end
-
list.sort!
-
-
# Take care of the broken text/xml entry by renaming or deleting it
-
text_xml = list.index("text/xml")
-
app_xml = list.index(Mime::XML.to_s)
-
-
if text_xml && app_xml
-
# set the q value to the max of the two
-
list[app_xml].q = [list[text_xml].q, list[app_xml].q].max
-
-
# make sure app_xml is ahead of text_xml in the list
-
if app_xml > text_xml
-
list[app_xml], list[text_xml] = list[text_xml], list[app_xml]
-
app_xml, text_xml = text_xml, app_xml
-
end
-
-
# delete text_xml from the list
-
list.delete_at(text_xml)
-
-
elsif text_xml
-
list[text_xml].name = Mime::XML.to_s
-
end
-
-
# Look for more specific XML-based types and sort them ahead of app/xml
-
-
if app_xml
-
idx = app_xml
-
app_xml_type = list[app_xml]
-
-
while(idx < list.length)
-
type = list[idx]
-
break if type.q < app_xml_type.q
-
if type.name =~ /\+xml$/
-
list[app_xml], list[idx] = list[idx], list[app_xml]
-
app_xml = idx
-
end
-
idx += 1
-
end
-
end
-
-
list.map! { |i| Mime::Type.lookup(i.name) }.uniq!
-
list
-
end
-
end
-
-
# input: 'text'
-
# returned value: [Mime::JSON, Mime::XML, Mime::ICS, Mime::HTML, Mime::CSS, Mime::CSV, Mime::JS, Mime::YAML, Mime::TEXT]
-
#
-
# input: 'application'
-
# returned value: [Mime::HTML, Mime::JS, Mime::XML, Mime::YAML, Mime::ATOM, Mime::JSON, Mime::RSS, Mime::URL_ENCODED_FORM]
-
1
def parse_data_with_trailing_star(input)
-
Mime::SET.select { |m| m =~ input }
-
end
-
-
# This method is opposite of register method.
-
#
-
# Usage:
-
#
-
# Mime::Type.unregister(:mobile)
-
1
def unregister(symbol)
-
symbol = symbol.to_s.upcase
-
mime = Mime.const_get(symbol)
-
Mime.instance_eval { remove_const(symbol) }
-
-
SET.delete_if { |v| v.eql?(mime) }
-
LOOKUP.delete_if { |k,v| v.eql?(mime) }
-
EXTENSION_LOOKUP.delete_if { |k,v| v.eql?(mime) }
-
end
-
end
-
-
1
def initialize(string, symbol = nil, synonyms = [])
-
14
@symbol, @synonyms = symbol, synonyms
-
14
@string = string
-
end
-
-
1
def to_s
-
@string
-
end
-
-
1
def to_str
-
to_s
-
end
-
-
1
def to_sym
-
13
@symbol
-
end
-
-
1
def ref
-
to_sym || to_s
-
end
-
-
1
def ===(list)
-
if list.is_a?(Array)
-
(@synonyms + [ self ]).any? { |synonym| list.include?(synonym) }
-
else
-
super
-
end
-
end
-
-
1
def ==(mime_type)
-
return false if mime_type.blank?
-
(@synonyms + [ self ]).any? do |synonym|
-
synonym.to_s == mime_type.to_s || synonym.to_sym == mime_type.to_sym
-
end
-
end
-
-
1
def =~(mime_type)
-
return false if mime_type.blank?
-
regexp = Regexp.new(Regexp.quote(mime_type.to_s))
-
(@synonyms + [ self ]).any? do |synonym|
-
synonym.to_s =~ regexp
-
end
-
end
-
-
# Returns true if Action Pack should check requests using this Mime Type for possible request forgery. See
-
# ActionController::RequestForgeryProtection.
-
1
def verify_request?
-
@@browser_generated_types.include?(to_sym)
-
end
-
-
1
def html?
-
@@html_types.include?(to_sym) || @string =~ /html/
-
end
-
-
1
private
-
1
def method_missing(method, *args)
-
if method.to_s =~ /(\w+)\?$/
-
$1.downcase.to_sym == to_sym
-
else
-
super
-
end
-
end
-
end
-
end
-
-
1
require 'action_dispatch/http/mime_types'
-
# Build list of Mime types for HTTP responses
-
# http://www.iana.org/assignments/media-types/
-
-
1
Mime::Type.register "text/html", :html, %w( application/xhtml+xml ), %w( xhtml )
-
1
Mime::Type.register "text/plain", :text, [], %w(txt)
-
1
Mime::Type.register "text/javascript", :js, %w( application/javascript application/x-javascript )
-
1
Mime::Type.register "text/css", :css
-
1
Mime::Type.register "text/calendar", :ics
-
1
Mime::Type.register "text/csv", :csv
-
1
Mime::Type.register "application/xml", :xml, %w( text/xml application/x-xml )
-
1
Mime::Type.register "application/rss+xml", :rss
-
1
Mime::Type.register "application/atom+xml", :atom
-
1
Mime::Type.register "application/x-yaml", :yaml, %w( text/yaml )
-
-
1
Mime::Type.register "multipart/form-data", :multipart_form
-
1
Mime::Type.register "application/x-www-form-urlencoded", :url_encoded_form
-
-
# http://www.ietf.org/rfc/rfc4627.txt
-
# http://www.json.org/JSONRequest.html
-
1
Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest )
-
-
# Create Mime::ALL but do not add it to the SET.
-
1
Mime::ALL = Mime::Type.new("*/*", :all, [])
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
-
1
module ActionDispatch
-
1
module Http
-
1
module Parameters
-
# Returns both GET and POST \parameters in a single hash.
-
1
def parameters
-
@env["action_dispatch.request.parameters"] ||= begin
-
params = request_parameters.merge(query_parameters)
-
params.merge!(path_parameters)
-
encode_params(params).with_indifferent_access
-
end
-
end
-
1
alias :params :parameters
-
-
1
def path_parameters=(parameters) #:nodoc:
-
@symbolized_path_params = nil
-
@env.delete("action_dispatch.request.parameters")
-
@env["action_dispatch.request.path_parameters"] = parameters
-
end
-
-
# The same as <tt>path_parameters</tt> with explicitly symbolized keys.
-
1
def symbolized_path_parameters
-
@symbolized_path_params ||= path_parameters.symbolize_keys
-
end
-
-
# Returns a hash with the \parameters used to form the \path of the request.
-
# Returned hash keys are strings:
-
#
-
# {'action' => 'my_action', 'controller' => 'my_controller'}
-
#
-
# See <tt>symbolized_path_parameters</tt> for symbolized keys.
-
1
def path_parameters
-
@env["action_dispatch.request.path_parameters"] ||= {}
-
end
-
-
1
private
-
-
# TODO: Validate that the characters are UTF-8. If they aren't,
-
# you'll get a weird error down the road, but our form handling
-
# should really prevent that from happening
-
1
def encode_params(params)
-
return params unless "ruby".encoding_aware?
-
-
if params.is_a?(String)
-
return params.force_encoding("UTF-8").encode!
-
elsif !params.is_a?(Hash)
-
return params
-
end
-
-
params.each do |k, v|
-
case v
-
when Hash
-
encode_params(v)
-
when Array
-
v.map! {|el| encode_params(el) }
-
else
-
encode_params(v)
-
end
-
end
-
end
-
-
# Convert nested Hash to HashWithIndifferentAccess
-
1
def normalize_parameters(value)
-
case value
-
when Hash
-
h = {}
-
value.each { |k, v| h[k] = normalize_parameters(v) }
-
h.with_indifferent_access
-
when Array
-
value.map { |e| normalize_parameters(e) }
-
else
-
value
-
end
-
end
-
end
-
end
-
end
-
1
require 'tempfile'
-
1
require 'stringio'
-
1
require 'strscan'
-
-
1
require 'active_support/core_ext/module/deprecation'
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
1
require 'active_support/core_ext/string/access'
-
1
require 'active_support/inflector'
-
1
require 'action_dispatch/http/headers'
-
-
1
module ActionDispatch
-
1
class Request < Rack::Request
-
1
include ActionDispatch::Http::Cache::Request
-
1
include ActionDispatch::Http::MimeNegotiation
-
1
include ActionDispatch::Http::Parameters
-
1
include ActionDispatch::Http::FilterParameters
-
1
include ActionDispatch::Http::Upload
-
1
include ActionDispatch::Http::URL
-
-
1
LOCALHOST = [/^127\.0\.0\.\d{1,3}$/, "::1", /^0:0:0:0:0:0:0:1(%.*)?$/].freeze
-
1
ENV_METHODS = %w[ AUTH_TYPE GATEWAY_INTERFACE
-
PATH_TRANSLATED REMOTE_HOST
-
REMOTE_IDENT REMOTE_USER REMOTE_ADDR
-
SERVER_NAME SERVER_PROTOCOL
-
-
HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
-
HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM
-
HTTP_NEGOTIATE HTTP_PRAGMA ].freeze
-
-
1
ENV_METHODS.each do |env|
-
17
class_eval <<-METHOD, __FILE__, __LINE__ + 1
-
def #{env.sub(/^HTTP_/n, '').downcase} # def accept_charset
-
@env["#{env}"] # @env["HTTP_ACCEPT_CHARSET"]
-
end # end
-
METHOD
-
end
-
-
1
def self.new(env)
-
if request = env["action_dispatch.request"] && request.instance_of?(self)
-
return request
-
end
-
-
super
-
end
-
-
1
def key?(key)
-
@env.key?(key)
-
end
-
-
# List of HTTP request methods from the following RFCs:
-
# Hypertext Transfer Protocol -- HTTP/1.1 (http://www.ietf.org/rfc/rfc2616.txt)
-
# HTTP Extensions for Distributed Authoring -- WEBDAV (http://www.ietf.org/rfc/rfc2518.txt)
-
# Versioning Extensions to WebDAV (http://www.ietf.org/rfc/rfc3253.txt)
-
# Ordered Collections Protocol (WebDAV) (http://www.ietf.org/rfc/rfc3648.txt)
-
# Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol (http://www.ietf.org/rfc/rfc3744.txt)
-
# Web Distributed Authoring and Versioning (WebDAV) SEARCH (http://www.ietf.org/rfc/rfc5323.txt)
-
# PATCH Method for HTTP (http://www.ietf.org/rfc/rfc5789.txt)
-
1
RFC2616 = %w(OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT)
-
1
RFC2518 = %w(PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK)
-
1
RFC3253 = %w(VERSION-CONTROL REPORT CHECKOUT CHECKIN UNCHECKOUT MKWORKSPACE UPDATE LABEL MERGE BASELINE-CONTROL MKACTIVITY)
-
1
RFC3648 = %w(ORDERPATCH)
-
1
RFC3744 = %w(ACL)
-
1
RFC5323 = %w(SEARCH)
-
1
RFC5789 = %w(PATCH)
-
-
1
HTTP_METHODS = RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC5789
-
1
HTTP_METHOD_LOOKUP = Hash.new { |h, m| h[m] = m.underscore.to_sym if HTTP_METHODS.include?(m) }
-
-
# Returns the HTTP \method that the application should see.
-
# In the case where the \method was overridden by a middleware
-
# (for instance, if a HEAD request was converted to a GET,
-
# or if a _method parameter was used to determine the \method
-
# the application should use), this \method returns the overridden
-
# value, not the original.
-
1
def request_method
-
@request_method ||= check_method(env["REQUEST_METHOD"])
-
end
-
-
# Returns a symbol form of the #request_method
-
1
def request_method_symbol
-
HTTP_METHOD_LOOKUP[request_method]
-
end
-
-
# Returns the original value of the environment's REQUEST_METHOD,
-
# even if it was overridden by middleware. See #request_method for
-
# more information.
-
1
def method
-
@method ||= check_method(env["rack.methodoverride.original_method"] || env['REQUEST_METHOD'])
-
end
-
-
# Returns a symbol form of the #method
-
1
def method_symbol
-
HTTP_METHOD_LOOKUP[method]
-
end
-
-
# Is this a GET (or HEAD) request?
-
# Equivalent to <tt>request.request_method == :get</tt>.
-
1
def get?
-
HTTP_METHOD_LOOKUP[request_method] == :get
-
end
-
-
# Is this a POST request?
-
# Equivalent to <tt>request.request_method == :post</tt>.
-
1
def post?
-
HTTP_METHOD_LOOKUP[request_method] == :post
-
end
-
-
# Is this a PUT request?
-
# Equivalent to <tt>request.request_method == :put</tt>.
-
1
def put?
-
HTTP_METHOD_LOOKUP[request_method] == :put
-
end
-
-
# Is this a DELETE request?
-
# Equivalent to <tt>request.request_method == :delete</tt>.
-
1
def delete?
-
HTTP_METHOD_LOOKUP[request_method] == :delete
-
end
-
-
# Is this a HEAD request?
-
# Equivalent to <tt>request.method == :head</tt>.
-
1
def head?
-
HTTP_METHOD_LOOKUP[method] == :head
-
end
-
-
# Provides access to the request's HTTP headers, for example:
-
#
-
# request.headers["Content-Type"] # => "text/plain"
-
1
def headers
-
Http::Headers.new(@env)
-
end
-
-
1
def fullpath
-
@fullpath ||= super
-
end
-
-
1
def forgery_whitelisted?
-
get?
-
end
-
1
deprecate :forgery_whitelisted? => "it is just an alias for 'get?' now, update your code"
-
-
1
def media_type
-
content_mime_type.to_s
-
end
-
-
# Returns the content length of the request as an integer.
-
1
def content_length
-
super.to_i
-
end
-
-
# Returns true if the "X-Requested-With" header contains "XMLHttpRequest"
-
# (case-insensitive). All major JavaScript libraries send this header with
-
# every Ajax request.
-
1
def xml_http_request?
-
@env['HTTP_X_REQUESTED_WITH'] =~ /XMLHttpRequest/i
-
end
-
1
alias :xhr? :xml_http_request?
-
-
1
def ip
-
@ip ||= super
-
end
-
-
# Which IP addresses are "trusted proxies" that can be stripped from
-
# the right-hand-side of X-Forwarded-For.
-
#
-
# http://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces.
-
1
TRUSTED_PROXIES = %r{
-
^127\.0\.0\.1$ | # localhost
-
^(10 | # private IP 10.x.x.x
-
172\.(1[6-9]|2[0-9]|3[0-1]) | # private IP in the range 172.16.0.0 .. 172.31.255.255
-
192\.168 # private IP 192.168.x.x
-
)\.
-
}x
-
-
# Determines originating IP address. REMOTE_ADDR is the standard
-
# but will fail if the user is behind a proxy. HTTP_CLIENT_IP and/or
-
# HTTP_X_FORWARDED_FOR are set by proxies so check for these if
-
# REMOTE_ADDR is a proxy. HTTP_X_FORWARDED_FOR may be a comma-
-
# delimited list in the case of multiple chained proxies; the last
-
# address which is not trusted is the originating IP.
-
1
def remote_ip
-
@remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s
-
end
-
-
# Returns the lowercase name of the HTTP server software.
-
1
def server_software
-
(@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil
-
end
-
-
# Read the request \body. This is useful for web services that need to
-
# work with raw requests directly.
-
1
def raw_post
-
unless @env.include? 'RAW_POST_DATA'
-
@env['RAW_POST_DATA'] = body.read(@env['CONTENT_LENGTH'].to_i)
-
body.rewind if body.respond_to?(:rewind)
-
end
-
@env['RAW_POST_DATA']
-
end
-
-
# The request body is an IO input stream. If the RAW_POST_DATA environment
-
# variable is already set, wrap it in a StringIO.
-
1
def body
-
if raw_post = @env['RAW_POST_DATA']
-
raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding)
-
StringIO.new(raw_post)
-
else
-
@env['rack.input']
-
end
-
end
-
-
1
def form_data?
-
FORM_DATA_MEDIA_TYPES.include?(content_mime_type.to_s)
-
end
-
-
1
def body_stream #:nodoc:
-
@env['rack.input']
-
end
-
-
# TODO This should be broken apart into AD::Request::Session and probably
-
# be included by the session middleware.
-
1
def reset_session
-
session.destroy if session && session.respond_to?(:destroy)
-
self.session = {}
-
@env['action_dispatch.request.flash_hash'] = nil
-
end
-
-
1
def session=(session) #:nodoc:
-
@env['rack.session'] = session
-
end
-
-
1
def session_options=(options)
-
@env['rack.session.options'] = options
-
end
-
-
# Override Rack's GET method to support indifferent access
-
1
def GET
-
@env["action_dispatch.request.query_parameters"] ||= (normalize_parameters(super) || {})
-
end
-
1
alias :query_parameters :GET
-
-
# Override Rack's POST method to support indifferent access
-
1
def POST
-
@env["action_dispatch.request.request_parameters"] ||= (normalize_parameters(super) || {})
-
end
-
1
alias :request_parameters :POST
-
-
-
# Returns the authorization header regardless of whether it was specified directly or through one of the
-
# proxy alternatives.
-
1
def authorization
-
@env['HTTP_AUTHORIZATION'] ||
-
@env['X-HTTP_AUTHORIZATION'] ||
-
@env['X_HTTP_AUTHORIZATION'] ||
-
@env['REDIRECT_X_HTTP_AUTHORIZATION']
-
end
-
-
# True if the request came from localhost, 127.0.0.1.
-
1
def local?
-
LOCALHOST.any? { |local_ip| local_ip === remote_addr && local_ip === remote_ip }
-
end
-
-
1
private
-
-
1
def check_method(name)
-
HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
-
name
-
end
-
end
-
end
-
1
require 'digest/md5'
-
1
require 'active_support/core_ext/module/delegation'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/class/attribute_accessors'
-
-
1
module ActionDispatch # :nodoc:
-
# Represents an HTTP response generated by a controller action. Use it to
-
# retrieve the current state of the response, or customize the response. It can
-
# either represent a real HTTP response (i.e. one that is meant to be sent
-
# back to the web browser) or a TestResponse (i.e. one that is generated
-
# from integration tests).
-
#
-
# \Response is mostly a Ruby on \Rails framework implementation detail, and
-
# should never be used directly in controllers. Controllers should use the
-
# methods defined in ActionController::Base instead. For example, if you want
-
# to set the HTTP response's content MIME type, then use
-
# ActionControllerBase#headers instead of Response#headers.
-
#
-
# Nevertheless, integration tests may want to inspect controller responses in
-
# more detail, and that's when \Response can be useful for application
-
# developers. Integration test methods such as
-
# ActionDispatch::Integration::Session#get and
-
# ActionDispatch::Integration::Session#post return objects of type
-
# TestResponse (which are of course also of type \Response).
-
#
-
# For example, the following demo integration test prints the body of the
-
# controller response to the console:
-
#
-
# class DemoControllerTest < ActionDispatch::IntegrationTest
-
# def test_print_root_path_to_console
-
# get('/')
-
# puts @response.body
-
# end
-
# end
-
1
class Response
-
1
attr_accessor :request, :header, :status
-
1
attr_writer :sending_file
-
-
1
alias_method :headers=, :header=
-
1
alias_method :headers, :header
-
-
1
delegate :[], :[]=, :to => :@header
-
1
delegate :each, :to => :@body
-
-
# Sets the HTTP response's content MIME type. For example, in the controller
-
# you could write this:
-
#
-
# response.content_type = "text/plain"
-
#
-
# If a character set has been defined for this response (see charset=) then
-
# the character set information will also be included in the content type
-
# information.
-
1
attr_accessor :charset, :content_type
-
-
1
CONTENT_TYPE = "Content-Type"
-
-
2
cattr_accessor(:default_charset) { "utf-8" }
-
-
1
include Rack::Response::Helpers
-
1
include ActionDispatch::Http::Cache::Response
-
-
1
def initialize(status = 200, header = {}, body = [])
-
self.body, self.header, self.status = body, header, status
-
-
@sending_file = false
-
@blank = false
-
-
if content_type = self["Content-Type"]
-
type, charset = content_type.split(/;\s*charset=/)
-
@content_type = Mime::Type.lookup(type)
-
@charset = charset || "UTF-8"
-
end
-
-
prepare_cache_control!
-
-
yield self if block_given?
-
end
-
-
1
def status=(status)
-
@status = Rack::Utils.status_code(status)
-
end
-
-
# The response code of the request
-
1
def response_code
-
@status
-
end
-
-
# Returns a String to ensure compatibility with Net::HTTPResponse
-
1
def code
-
@status.to_s
-
end
-
-
1
def message
-
Rack::Utils::HTTP_STATUS_CODES[@status]
-
end
-
1
alias_method :status_message, :message
-
-
1
def respond_to?(method)
-
if method.to_sym == :to_path
-
@body.respond_to?(:to_path)
-
else
-
super
-
end
-
end
-
-
1
def to_path
-
@body.to_path
-
end
-
-
1
def body
-
str = ''
-
each { |part| str << part.to_s }
-
str
-
end
-
-
1
EMPTY = " "
-
-
1
class BodyBuster #:nodoc:
-
1
def initialize(response)
-
@response = response
-
@body = ""
-
end
-
-
1
def bust(body)
-
body.call(@response, self)
-
body.close if body.respond_to?(:close)
-
@body
-
end
-
-
1
def write(string)
-
@body << string.to_s
-
end
-
end
-
-
1
def body=(body)
-
@blank = true if body == EMPTY
-
-
if body.respond_to?(:call)
-
ActiveSupport::Deprecation.warn "Setting a Proc or an object that responds to call " \
-
"in response_body is no longer supported", caller
-
body = BodyBuster.new(self).bust(body)
-
end
-
-
# Explicitly check for strings. This is *wrong* theoretically
-
# but if we don't check this, the performance on string bodies
-
# is bad on Ruby 1.8 (because strings responds to each then).
-
@body = if body.respond_to?(:to_str) || !body.respond_to?(:each)
-
[body]
-
else
-
body
-
end
-
end
-
-
1
def body_parts
-
@body
-
end
-
-
1
def set_cookie(key, value)
-
::Rack::Utils.set_cookie_header!(header, key, value)
-
end
-
-
1
def delete_cookie(key, value={})
-
::Rack::Utils.delete_cookie_header!(header, key, value)
-
end
-
-
1
def location
-
headers['Location']
-
end
-
1
alias_method :redirect_url, :location
-
-
1
def location=(url)
-
headers['Location'] = url
-
end
-
-
1
def close
-
@body.close if @body.respond_to?(:close)
-
end
-
-
1
def to_a
-
assign_default_content_type_and_charset!
-
handle_conditional_get!
-
-
@header["Set-Cookie"] = @header["Set-Cookie"].join("\n") if @header["Set-Cookie"].respond_to?(:join)
-
-
if [204, 304].include?(@status)
-
@header.delete "Content-Type"
-
[@status, @header, []]
-
else
-
[@status, @header, self]
-
end
-
end
-
1
alias prepare! to_a
-
1
alias to_ary to_a # For implicit splat on 1.9.2
-
-
# Returns the response cookies, converted to a Hash of (name => value) pairs
-
#
-
# assert_equal 'AuthorOfNewPage', r.cookies['author']
-
1
def cookies
-
cookies = {}
-
if header = self["Set-Cookie"]
-
header = header.split("\n") if header.respond_to?(:to_str)
-
header.each do |cookie|
-
if pair = cookie.split(';').first
-
key, value = pair.split("=").map { |v| Rack::Utils.unescape(v) }
-
cookies[key] = value
-
end
-
end
-
end
-
cookies
-
end
-
-
1
private
-
-
1
def assign_default_content_type_and_charset!
-
return if headers[CONTENT_TYPE].present?
-
-
@content_type ||= Mime::HTML
-
@charset ||= self.class.default_charset
-
-
type = @content_type.to_s.dup
-
type << "; charset=#{@charset}" unless @sending_file
-
-
headers[CONTENT_TYPE] = type
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
module Http
-
1
class UploadedFile
-
1
attr_accessor :original_filename, :content_type, :tempfile, :headers
-
-
1
def initialize(hash)
-
@original_filename = encode_filename(hash[:filename])
-
@content_type = hash[:type]
-
@headers = hash[:head]
-
@tempfile = hash[:tempfile]
-
raise(ArgumentError, ':tempfile is required') unless @tempfile
-
end
-
-
1
def open
-
@tempfile.open
-
end
-
-
1
def path
-
@tempfile.path
-
end
-
-
1
def read(*args)
-
@tempfile.read(*args)
-
end
-
-
1
def rewind
-
@tempfile.rewind
-
end
-
-
1
def size
-
@tempfile.size
-
end
-
-
1
private
-
1
def encode_filename(filename)
-
# Encode the filename in the utf8 encoding, unless it is nil or we're in 1.8
-
if "ruby".encoding_aware? && filename
-
filename.force_encoding("UTF-8").encode!
-
else
-
filename
-
end
-
end
-
end
-
-
1
module Upload
-
# Convert nested Hash to HashWithIndifferentAccess and replace
-
# file upload hash with UploadedFile objects
-
1
def normalize_parameters(value)
-
if Hash === value && value.has_key?(:tempfile)
-
UploadedFile.new(value)
-
else
-
super
-
end
-
end
-
1
private :normalize_parameters
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
module Http
-
1
module URL
-
1
mattr_accessor :tld_length
-
1
self.tld_length = 1
-
-
1
class << self
-
1
def extract_domain(host, tld_length = @@tld_length)
-
return nil unless named_host?(host)
-
host.split('.').last(1 + tld_length).join('.')
-
end
-
-
1
def extract_subdomains(host, tld_length = @@tld_length)
-
return [] unless named_host?(host)
-
parts = host.split('.')
-
parts[0..-(tld_length+2)]
-
end
-
-
1
def extract_subdomain(host, tld_length = @@tld_length)
-
extract_subdomains(host, tld_length).join('.')
-
end
-
-
1
def url_for(options = {})
-
unless options[:host].present? || options[:only_path].present?
-
raise ArgumentError, 'Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true'
-
end
-
-
rewritten_url = ""
-
-
unless options[:only_path]
-
unless options[:protocol] == false
-
rewritten_url << (options[:protocol] || "http")
-
rewritten_url << ":" unless rewritten_url.match(%r{:|//})
-
end
-
rewritten_url << "//" unless rewritten_url.match("//")
-
rewritten_url << rewrite_authentication(options)
-
rewritten_url << host_or_subdomain_and_domain(options)
-
rewritten_url << ":#{options.delete(:port)}" if options[:port]
-
end
-
-
path = options.delete(:path) || ''
-
-
params = options[:params] || {}
-
params.reject! {|k,v| v.to_param.nil? }
-
-
rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
-
rewritten_url << "?#{params.to_query}" unless params.empty?
-
rewritten_url << "##{Rack::Mount::Utils.escape_uri(options[:anchor].to_param.to_s)}" if options[:anchor]
-
rewritten_url
-
end
-
-
1
private
-
-
1
def named_host?(host)
-
!(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
-
end
-
-
1
def rewrite_authentication(options)
-
if options[:user] && options[:password]
-
"#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
-
else
-
""
-
end
-
end
-
-
1
def host_or_subdomain_and_domain(options)
-
return options[:host] unless options[:subdomain] || options[:domain]
-
-
tld_length = options[:tld_length] || @@tld_length
-
-
host = ""
-
host << (options[:subdomain] || extract_subdomain(options[:host], tld_length))
-
host << "."
-
host << (options[:domain] || extract_domain(options[:host], tld_length))
-
host
-
end
-
end
-
-
# Returns the complete URL used for this request.
-
1
def url
-
protocol + host_with_port + fullpath
-
end
-
-
# Returns 'https://' if this is an SSL request and 'http://' otherwise.
-
1
def protocol
-
@protocol ||= ssl? ? 'https://' : 'http://'
-
end
-
-
# Returns the \host for this request, such as "example.com".
-
1
def raw_host_with_port
-
if forwarded = env["HTTP_X_FORWARDED_HOST"]
-
forwarded.split(/,\s?/).last
-
else
-
env['HTTP_HOST'] || "#{env['SERVER_NAME'] || env['SERVER_ADDR']}:#{env['SERVER_PORT']}"
-
end
-
end
-
-
# Returns the host for this request, such as example.com.
-
1
def host
-
raw_host_with_port.sub(/:\d+$/, '')
-
end
-
-
# Returns a \host:\port string for this request, such as "example.com" or
-
# "example.com:8080".
-
1
def host_with_port
-
"#{host}#{port_string}"
-
end
-
-
# Returns the port number of this request as an integer.
-
1
def port
-
@port ||= begin
-
if raw_host_with_port =~ /:(\d+)$/
-
$1.to_i
-
else
-
standard_port
-
end
-
end
-
end
-
-
# Returns the standard \port number for this request's protocol.
-
1
def standard_port
-
case protocol
-
when 'https://' then 443
-
else 80
-
end
-
end
-
-
# Returns whether this request is using the standard port
-
1
def standard_port?
-
port == standard_port
-
end
-
-
# Returns a number \port suffix like 8080 if the \port number of this request
-
# is not the default HTTP \port 80 or HTTPS \port 443.
-
1
def optional_port
-
standard_port? ? nil : port
-
end
-
-
# Returns a string \port suffix, including colon, like ":8080" if the \port
-
# number of this request is not the default HTTP \port 80 or HTTPS \port 443.
-
1
def port_string
-
standard_port? ? '' : ":#{port}"
-
end
-
-
1
def server_port
-
@env['SERVER_PORT'].to_i
-
end
-
-
# Returns the \domain part of a \host, such as "rubyonrails.org" in "www.rubyonrails.org". You can specify
-
# a different <tt>tld_length</tt>, such as 2 to catch rubyonrails.co.uk in "www.rubyonrails.co.uk".
-
1
def domain(tld_length = @@tld_length)
-
ActionDispatch::Http::URL.extract_domain(host, tld_length)
-
end
-
-
# Returns all the \subdomains as an array, so <tt>["dev", "www"]</tt> would be
-
# returned for "dev.www.rubyonrails.org". You can specify a different <tt>tld_length</tt>,
-
# such as 2 to catch <tt>["www"]</tt> instead of <tt>["www", "rubyonrails"]</tt>
-
# in "www.rubyonrails.co.uk".
-
1
def subdomains(tld_length = @@tld_length)
-
ActionDispatch::Http::URL.extract_subdomains(host, tld_length)
-
end
-
-
# Returns all the \subdomains as a string, so <tt>"dev.www"</tt> would be
-
# returned for "dev.www.rubyonrails.org". You can specify a different <tt>tld_length</tt>,
-
# such as 2 to catch <tt>"www"</tt> instead of <tt>"www.rubyonrails"</tt>
-
# in "www.rubyonrails.co.uk".
-
1
def subdomain(tld_length = @@tld_length)
-
subdomains(tld_length).join(".")
-
end
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
class BestStandardsSupport
-
1
def initialize(app, type = true)
-
1
@app = app
-
-
1
@header = case type
-
when true
-
1
"IE=Edge,chrome=1"
-
when :builtin
-
"IE=Edge"
-
when false
-
nil
-
end
-
end
-
-
1
def call(env)
-
status, headers, body = @app.call(env)
-
headers["X-UA-Compatible"] = @header
-
[status, headers, body]
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/delegation'
-
-
1
module ActionDispatch
-
# Provide callbacks to be executed before and after the request dispatch.
-
1
class Callbacks
-
1
include ActiveSupport::Callbacks
-
-
1
define_callbacks :call, :rescuable => true
-
-
1
class << self
-
1
delegate :to_prepare, :to_cleanup, :to => "ActionDispatch::Reloader"
-
end
-
-
1
def self.before(*args, &block)
-
set_callback(:call, :before, *args, &block)
-
end
-
-
1
def self.after(*args, &block)
-
set_callback(:call, :after, *args, &block)
-
end
-
-
1
def initialize(app, unused = nil)
-
1
ActiveSupport::Deprecation.warn "Passing a second argument to ActionDispatch::Callbacks.new is deprecated." unless unused.nil?
-
1
@app = app
-
end
-
-
1
def call(env)
-
run_callbacks :call do
-
@app.call(env)
-
end
-
end
-
end
-
end
-
1
require "active_support/core_ext/object/blank"
-
-
1
module ActionDispatch
-
1
class Request
-
1
def cookie_jar
-
env['action_dispatch.cookies'] ||= Cookies::CookieJar.build(self)
-
end
-
end
-
-
# \Cookies are read and written through ActionController#cookies.
-
#
-
# The cookies being read are the ones received along with the request, the cookies
-
# being written will be sent out with the response. Reading a cookie does not get
-
# the cookie object itself back, just the value it holds.
-
#
-
# Examples for writing:
-
#
-
# # Sets a simple session cookie.
-
# # This cookie will be deleted when the user's browser is closed.
-
# cookies[:user_name] = "david"
-
#
-
# # Assign an array of values to a cookie.
-
# cookies[:lat_lon] = [47.68, -122.37]
-
#
-
# # Sets a cookie that expires in 1 hour.
-
# cookies[:login] = { :value => "XJ-122", :expires => 1.hour.from_now }
-
#
-
# # Sets a signed cookie, which prevents a user from tampering with its value.
-
# # The cookie is signed by your app's <tt>config.secret_token</tt> value.
-
# # Rails generates this value by default when you create a new Rails app.
-
# cookies.signed[:user_id] = current_user.id
-
#
-
# # Sets a "permanent" cookie (which expires in 20 years from now).
-
# cookies.permanent[:login] = "XJ-122"
-
#
-
# # You can also chain these methods:
-
# cookies.permanent.signed[:login] = "XJ-122"
-
#
-
# Examples for reading:
-
#
-
# cookies[:user_name] # => "david"
-
# cookies.size # => 2
-
# cookies[:lat_lon] # => [47.68, -122.37]
-
#
-
# Example for deleting:
-
#
-
# cookies.delete :user_name
-
#
-
# Please note that if you specify a :domain when setting a cookie, you must also specify the domain when deleting the cookie:
-
#
-
# cookies[:key] = {
-
# :value => 'a yummy cookie',
-
# :expires => 1.year.from_now,
-
# :domain => 'domain.com'
-
# }
-
#
-
# cookies.delete(:key, :domain => 'domain.com')
-
#
-
# The option symbols for setting cookies are:
-
#
-
# * <tt>:value</tt> - The cookie's value or list of values (as an array).
-
# * <tt>:path</tt> - The path for which this cookie applies. Defaults to the root
-
# of the application.
-
# * <tt>:domain</tt> - The domain for which this cookie applies so you can
-
# restrict to the domain level. If you use a schema like www.example.com
-
# and want to share session with user.example.com set <tt>:domain</tt>
-
# to <tt>:all</tt>. Make sure to specify the <tt>:domain</tt> option with
-
# <tt>:all</tt> again when deleting keys.
-
#
-
# :domain => nil # Does not sets cookie domain. (default)
-
# :domain => :all # Allow the cookie for the top most level
-
# domain and subdomains.
-
#
-
# * <tt>:expires</tt> - The time at which this cookie expires, as a \Time object.
-
# * <tt>:secure</tt> - Whether this cookie is a only transmitted to HTTPS servers.
-
# Default is +false+.
-
# * <tt>:httponly</tt> - Whether this cookie is accessible via scripting or
-
# only HTTP. Defaults to +false+.
-
1
class Cookies
-
1
HTTP_HEADER = "Set-Cookie".freeze
-
1
TOKEN_KEY = "action_dispatch.secret_token".freeze
-
-
# Raised when storing more than 4K of session data.
-
1
class CookieOverflow < StandardError; end
-
-
1
class CookieJar #:nodoc:
-
-
# This regular expression is used to split the levels of a domain.
-
# The top level domain can be any string without a period or
-
# **.**, ***.** style TLDs like co.uk or com.au
-
#
-
# www.example.co.uk gives:
-
# $& => example.co.uk
-
#
-
# example.com gives:
-
# $& => example.com
-
#
-
# lots.of.subdomains.example.local gives:
-
# $& => example.local
-
1
DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/
-
-
1
def self.build(request)
-
secret = request.env[TOKEN_KEY]
-
host = request.host
-
secure = request.ssl?
-
-
new(secret, host, secure).tap do |hash|
-
hash.update(request.cookies)
-
end
-
end
-
-
1
def initialize(secret = nil, host = nil, secure = false)
-
@secret = secret
-
@set_cookies = {}
-
@delete_cookies = {}
-
@host = host
-
@secure = secure
-
@closed = false
-
@cookies = {}
-
end
-
-
1
attr_reader :closed
-
1
alias :closed? :closed
-
1
def close!; @closed = true end
-
-
# Returns the value of the cookie by +name+, or +nil+ if no such cookie exists.
-
1
def [](name)
-
@cookies[name.to_s]
-
end
-
-
1
def key?(name)
-
@cookies.key?(name.to_s)
-
end
-
1
alias :has_key? :key?
-
-
1
def update(other_hash)
-
@cookies.update other_hash
-
self
-
end
-
-
1
def handle_options(options) #:nodoc:
-
options[:path] ||= "/"
-
-
if options[:domain] == :all
-
# if there is a provided tld length then we use it otherwise default domain regexp
-
domain_regexp = options[:tld_length] ? /([^.]+\.?){#{options[:tld_length]}}$/ : DOMAIN_REGEXP
-
-
# if host is not ip and matches domain regexp
-
# (ip confirms to domain regexp so we explicitly check for ip)
-
options[:domain] = if (@host !~ /^[\d.]+$/) && (@host =~ domain_regexp)
-
".#{$&}"
-
end
-
elsif options[:domain].is_a? Array
-
# if host matches one of the supplied domains without a dot in front of it
-
options[:domain] = options[:domain].find {|domain| @host.include? domain[/^\.?(.*)$/, 1] }
-
end
-
end
-
-
# Sets the cookie named +name+. The second argument may be the very cookie
-
# value, or a hash of options as documented above.
-
1
def []=(key, options)
-
raise ClosedError, :cookies if closed?
-
if options.is_a?(Hash)
-
options.symbolize_keys!
-
value = options[:value]
-
else
-
value = options
-
options = { :value => value }
-
end
-
-
value = @cookies[key.to_s] = value
-
-
handle_options(options)
-
-
@set_cookies[key.to_s] = options
-
@delete_cookies.delete(key.to_s)
-
value
-
end
-
-
# Removes the cookie on the client machine by setting the value to an empty string
-
# and setting its expiration date into the past. Like <tt>[]=</tt>, you can pass in
-
# an options hash to delete cookies with extra data such as a <tt>:path</tt>.
-
1
def delete(key, options = {})
-
options.symbolize_keys!
-
-
handle_options(options)
-
-
value = @cookies.delete(key.to_s)
-
@delete_cookies[key.to_s] = options
-
value
-
end
-
-
# Returns a jar that'll automatically set the assigned cookies to have an expiration date 20 years from now. Example:
-
#
-
# cookies.permanent[:prefers_open_id] = true
-
# # => Set-Cookie: prefers_open_id=true; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
-
#
-
# This jar is only meant for writing. You'll read permanent cookies through the regular accessor.
-
#
-
# This jar allows chaining with the signed jar as well, so you can set permanent, signed cookies. Examples:
-
#
-
# cookies.permanent.signed[:remember_me] = current_user.id
-
# # => Set-Cookie: remember_me=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
-
1
def permanent
-
@permanent ||= PermanentCookieJar.new(self, @secret)
-
end
-
-
# Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from
-
# the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed
-
# cookie was tampered with by the user (or a 3rd party), an ActiveSupport::MessageVerifier::InvalidSignature exception will
-
# be raised.
-
#
-
# This jar requires that you set a suitable secret for the verification on your app's config.secret_token.
-
#
-
# Example:
-
#
-
# cookies.signed[:discount] = 45
-
# # => Set-Cookie: discount=BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7; path=/
-
#
-
# cookies.signed[:discount] # => 45
-
1
def signed
-
@signed ||= SignedCookieJar.new(self, @secret)
-
end
-
-
1
def write(headers)
-
@set_cookies.each { |k, v| ::Rack::Utils.set_cookie_header!(headers, k, v) if write_cookie?(v) }
-
@delete_cookies.each { |k, v| ::Rack::Utils.delete_cookie_header!(headers, k, v) }
-
end
-
-
1
private
-
-
1
def write_cookie?(cookie)
-
@secure || !cookie[:secure] || defined?(Rails.env) && Rails.env.development?
-
end
-
end
-
-
1
class PermanentCookieJar < CookieJar #:nodoc:
-
1
def initialize(parent_jar, secret)
-
@parent_jar, @secret = parent_jar, secret
-
end
-
-
1
def []=(key, options)
-
raise ClosedError, :cookies if closed?
-
if options.is_a?(Hash)
-
options.symbolize_keys!
-
else
-
options = { :value => options }
-
end
-
-
options[:expires] = 20.years.from_now
-
@parent_jar[key] = options
-
end
-
-
1
def signed
-
@signed ||= SignedCookieJar.new(self, @secret)
-
end
-
-
1
def method_missing(method, *arguments, &block)
-
@parent_jar.send(method, *arguments, &block)
-
end
-
end
-
-
1
class SignedCookieJar < CookieJar #:nodoc:
-
1
MAX_COOKIE_SIZE = 4096 # Cookies can typically store 4096 bytes.
-
1
SECRET_MIN_LENGTH = 30 # Characters
-
-
1
def initialize(parent_jar, secret)
-
ensure_secret_secure(secret)
-
@parent_jar = parent_jar
-
@verifier = ActiveSupport::MessageVerifier.new(secret)
-
end
-
-
1
def [](name)
-
if signed_message = @parent_jar[name]
-
@verifier.verify(signed_message)
-
end
-
rescue ActiveSupport::MessageVerifier::InvalidSignature
-
nil
-
end
-
-
1
def []=(key, options)
-
raise ClosedError, :cookies if closed?
-
if options.is_a?(Hash)
-
options.symbolize_keys!
-
options[:value] = @verifier.generate(options[:value])
-
else
-
options = { :value => @verifier.generate(options) }
-
end
-
-
raise CookieOverflow if options[:value].size > MAX_COOKIE_SIZE
-
@parent_jar[key] = options
-
end
-
-
1
def method_missing(method, *arguments, &block)
-
@parent_jar.send(method, *arguments, &block)
-
end
-
-
1
protected
-
-
# To prevent users from using something insecure like "Password" we make sure that the
-
# secret they've provided is at least 30 characters in length.
-
1
def ensure_secret_secure(secret)
-
if secret.blank?
-
raise ArgumentError, "A secret is required to generate an " +
-
"integrity hash for cookie session data. Use " +
-
"config.secret_token = \"some secret phrase of at " +
-
"least #{SECRET_MIN_LENGTH} characters\"" +
-
"in config/initializers/secret_token.rb"
-
end
-
-
if secret.length < SECRET_MIN_LENGTH
-
raise ArgumentError, "Secret should be something secure, " +
-
"like \"#{SecureRandom.hex(16)}\". The value you " +
-
"provided, \"#{secret}\", is shorter than the minimum length " +
-
"of #{SECRET_MIN_LENGTH} characters"
-
end
-
end
-
end
-
-
1
def initialize(app)
-
1
@app = app
-
end
-
-
1
def call(env)
-
cookie_jar = nil
-
status, headers, body = @app.call(env)
-
-
if cookie_jar = env['action_dispatch.cookies']
-
cookie_jar.write(headers)
-
if headers[HTTP_HEADER].respond_to?(:join)
-
headers[HTTP_HEADER] = headers[HTTP_HEADER].join("\n")
-
end
-
end
-
-
[status, headers, body]
-
ensure
-
cookie_jar = ActionDispatch::Request.new(env).cookie_jar unless cookie_jar
-
cookie_jar.close!
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
class Request
-
# Access the contents of the flash. Use <tt>flash["notice"]</tt> to
-
# read a notice you put there or <tt>flash["notice"] = "hello"</tt>
-
# to put a new one.
-
1
def flash
-
@env[Flash::KEY] ||= (session["flash"] || Flash::FlashHash.new)
-
end
-
end
-
-
# The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed
-
# to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
-
# action that sets <tt>flash[:notice] = "Post successfully created"</tt> before redirecting to a display action that can
-
# then expose the flash to its template. Actually, that exposure is automatically done. Example:
-
#
-
# class PostsController < ActionController::Base
-
# def create
-
# # save post
-
# flash[:notice] = "Post successfully created"
-
# redirect_to posts_path(@post)
-
# end
-
#
-
# def show
-
# # doesn't need to assign the flash notice to the template, that's done automatically
-
# end
-
# end
-
#
-
# show.html.erb
-
# <% if flash[:notice] %>
-
# <div class="notice"><%= flash[:notice] %></div>
-
# <% end %>
-
#
-
# Since the +notice+ and +alert+ keys are a common idiom, convenience accessors are available:
-
#
-
# flash.alert = "You must be logged in"
-
# flash.notice = "Post successfully created"
-
#
-
# This example just places a string in the flash, but you can put any object in there. And of course, you can put as
-
# many as you like at a time too. Just remember: They'll be gone by the time the next action has been performed.
-
#
-
# See docs on the FlashHash class for more details about the flash.
-
1
class Flash
-
1
KEY = 'action_dispatch.request.flash_hash'.freeze
-
-
1
class FlashNow #:nodoc:
-
1
attr_accessor :flash
-
-
1
def initialize(flash)
-
@flash = flash
-
end
-
-
1
def []=(k, v)
-
@flash[k] = v
-
@flash.discard(k)
-
v
-
end
-
-
1
def [](k)
-
@flash[k]
-
end
-
-
# Convenience accessor for flash.now[:alert]=
-
1
def alert=(message)
-
self[:alert] = message
-
end
-
-
# Convenience accessor for flash.now[:notice]=
-
1
def notice=(message)
-
self[:notice] = message
-
end
-
end
-
-
1
class FlashHash
-
1
include Enumerable
-
-
1
def initialize #:nodoc:
-
@used = Set.new
-
@closed = false
-
@flashes = {}
-
@now = nil
-
end
-
-
1
def initialize_copy(other)
-
if other.now_is_loaded?
-
@now = other.now.dup
-
@now.flash = self
-
end
-
super
-
end
-
-
1
def []=(k, v) #:nodoc:
-
raise ClosedError, :flash if closed?
-
keep(k)
-
@flashes[k] = v
-
end
-
-
1
def [](k)
-
@flashes[k]
-
end
-
-
1
def update(h) #:nodoc:
-
h.keys.each { |k| keep(k) }
-
@flashes.update h
-
self
-
end
-
-
1
def keys
-
@flashes.keys
-
end
-
-
1
def key?(name)
-
@flashes.key? name
-
end
-
-
1
def delete(key)
-
@flashes.delete key
-
self
-
end
-
-
1
def to_hash
-
@flashes.dup
-
end
-
-
1
def empty?
-
@flashes.empty?
-
end
-
-
1
def clear
-
@flashes.clear
-
end
-
-
1
def each(&block)
-
@flashes.each(&block)
-
end
-
-
1
alias :merge! :update
-
-
1
def replace(h) #:nodoc:
-
@used = Set.new
-
@flashes.replace h
-
self
-
end
-
-
# Sets a flash that will not be available to the next action, only to the current.
-
#
-
# flash.now[:message] = "Hello current action"
-
#
-
# This method enables you to use the flash as a central messaging system in your app.
-
# When you need to pass an object to the next action, you use the standard flash assign (<tt>[]=</tt>).
-
# When you need to pass an object to the current action, you use <tt>now</tt>, and your object will
-
# vanish when the current action is done.
-
#
-
# Entries set via <tt>now</tt> are accessed the same way as standard entries: <tt>flash['my-key']</tt>.
-
1
def now
-
@now ||= FlashNow.new(self)
-
end
-
-
1
attr_reader :closed
-
1
alias :closed? :closed
-
1
def close!; @closed = true; end
-
-
# Keeps either the entire current flash or a specific flash entry available for the next action:
-
#
-
# flash.keep # keeps the entire flash
-
# flash.keep(:notice) # keeps only the "notice" entry, the rest of the flash is discarded
-
1
def keep(k = nil)
-
use(k, false)
-
end
-
-
# Marks the entire flash or a single flash entry to be discarded by the end of the current action:
-
#
-
# flash.discard # discard the entire flash at the end of the current action
-
# flash.discard(:warning) # discard only the "warning" entry at the end of the current action
-
1
def discard(k = nil)
-
use(k)
-
end
-
-
# Mark for removal entries that were kept, and delete unkept ones.
-
#
-
# This method is called automatically by filters, so you generally don't need to care about it.
-
1
def sweep #:nodoc:
-
keys.each do |k|
-
unless @used.include?(k)
-
@used << k
-
else
-
delete(k)
-
@used.delete(k)
-
end
-
end
-
-
# clean up after keys that could have been left over by calling reject! or shift on the flash
-
(@used - keys).each{ |k| @used.delete(k) }
-
end
-
-
# Convenience accessor for flash[:alert]
-
1
def alert
-
self[:alert]
-
end
-
-
# Convenience accessor for flash[:alert]=
-
1
def alert=(message)
-
self[:alert] = message
-
end
-
-
# Convenience accessor for flash[:notice]
-
1
def notice
-
self[:notice]
-
end
-
-
# Convenience accessor for flash[:notice]=
-
1
def notice=(message)
-
self[:notice] = message
-
end
-
-
1
protected
-
-
1
def now_is_loaded?
-
!!@now
-
end
-
-
# Used internally by the <tt>keep</tt> and <tt>discard</tt> methods
-
# use() # marks the entire flash as used
-
# use('msg') # marks the "msg" entry as used
-
# use(nil, false) # marks the entire flash as unused (keeps it around for one more action)
-
# use('msg', false) # marks the "msg" entry as unused (keeps it around for one more action)
-
# Returns the single value for the key you asked to be marked (un)used or the FlashHash itself
-
# if no key is passed.
-
1
def use(key = nil, used = true)
-
Array(key || keys).each { |k| used ? @used << k : @used.delete(k) }
-
return key ? self[key] : self
-
end
-
end
-
-
1
def initialize(app)
-
1
@app = app
-
end
-
-
1
def call(env)
-
if (session = env['rack.session']) && (flash = session['flash'])
-
flash.sweep
-
end
-
-
@app.call(env)
-
ensure
-
session = env['rack.session'] || {}
-
flash_hash = env[KEY]
-
-
if flash_hash
-
if !flash_hash.empty? || session.key?('flash')
-
session["flash"] = flash_hash
-
new_hash = flash_hash.dup
-
else
-
new_hash = flash_hash
-
end
-
-
env[KEY] = new_hash
-
new_hash.close!
-
end
-
-
if session.key?('flash') && session['flash'].empty?
-
session.delete('flash')
-
end
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
class Head
-
1
def initialize(app)
-
1
@app = app
-
end
-
-
1
def call(env)
-
if env["REQUEST_METHOD"] == "HEAD"
-
env["REQUEST_METHOD"] = "GET"
-
env["rack.methodoverride.original_method"] = "HEAD"
-
status, headers, body = @app.call(env)
-
[status, headers, []]
-
else
-
@app.call(env)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/conversions'
-
1
require 'action_dispatch/http/request'
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
-
1
module ActionDispatch
-
1
class ParamsParser
-
1
DEFAULT_PARSERS = {
-
Mime::XML => :xml_simple,
-
Mime::JSON => :json
-
}
-
-
1
def initialize(app, parsers = {})
-
1
@app, @parsers = app, DEFAULT_PARSERS.merge(parsers)
-
end
-
-
1
def call(env)
-
if params = parse_formatted_parameters(env)
-
env["action_dispatch.request.request_parameters"] = params
-
end
-
-
@app.call(env)
-
end
-
-
1
private
-
1
def parse_formatted_parameters(env)
-
request = Request.new(env)
-
-
return false if request.content_length.zero?
-
-
mime_type = content_type_from_legacy_post_data_format_header(env) ||
-
request.content_mime_type
-
-
strategy = @parsers[mime_type]
-
-
return false unless strategy
-
-
case strategy
-
when Proc
-
strategy.call(request.raw_post)
-
when :xml_simple, :xml_node
-
data = Hash.from_xml(request.body.read) || {}
-
request.body.rewind if request.body.respond_to?(:rewind)
-
data.with_indifferent_access
-
when :yaml
-
YAML.load(request.raw_post)
-
when :json
-
data = ActiveSupport::JSON.decode(request.body)
-
request.body.rewind if request.body.respond_to?(:rewind)
-
data = {:_json => data} unless data.is_a?(Hash)
-
data.with_indifferent_access
-
else
-
false
-
end
-
rescue Exception => e # YAML, XML or Ruby code block errors
-
logger.debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}"
-
-
raise
-
{ "body" => request.raw_post,
-
"content_type" => request.content_mime_type,
-
"content_length" => request.content_length,
-
"exception" => "#{e.message} (#{e.class})",
-
"backtrace" => e.backtrace }
-
end
-
-
1
def content_type_from_legacy_post_data_format_header(env)
-
if x_post_format = env['HTTP_X_POST_DATA_FORMAT']
-
case x_post_format.to_s.downcase
-
when 'yaml' then return Mime::YAML
-
when 'xml' then return Mime::XML
-
end
-
end
-
-
nil
-
end
-
-
1
def logger
-
defined?(Rails.logger) ? Rails.logger : Logger.new($stderr)
-
end
-
end
-
end
-
1
module ActionDispatch
-
# ActionDispatch::Reloader provides prepare and cleanup callbacks,
-
# intended to assist with code reloading during development.
-
#
-
# Prepare callbacks are run before each request, and cleanup callbacks
-
# after each request. In this respect they are analogs of ActionDispatch::Callback's
-
# before and after callbacks. However, cleanup callbacks are not called until the
-
# request is fully complete -- that is, after #close has been called on
-
# the response body. This is important for streaming responses such as the
-
# following:
-
#
-
# self.response_body = lambda { |response, output|
-
# # code here which refers to application models
-
# }
-
#
-
# Cleanup callbacks will not be called until after the response_body lambda
-
# is evaluated, ensuring that it can refer to application models and other
-
# classes before they are unloaded.
-
#
-
# By default, ActionDispatch::Reloader is included in the middleware stack
-
# only in the development environment; specifically, when config.cache_classes
-
# is false. Callbacks may be registered even when it is not included in the
-
# middleware stack, but are executed only when +ActionDispatch::Reloader.prepare!+
-
# or +ActionDispatch::Reloader.cleanup!+ are called manually.
-
#
-
1
class Reloader
-
1
include ActiveSupport::Callbacks
-
-
1
define_callbacks :prepare, :scope => :name
-
1
define_callbacks :cleanup, :scope => :name
-
-
# Add a prepare callback. Prepare callbacks are run before each request, prior
-
# to ActionDispatch::Callback's before callbacks.
-
1
def self.to_prepare(*args, &block)
-
3
set_callback(:prepare, *args, &block)
-
end
-
-
# Add a cleanup callback. Cleanup callbacks are run after each request is
-
# complete (after #close is called on the response body).
-
1
def self.to_cleanup(*args, &block)
-
2
set_callback(:cleanup, *args, &block)
-
end
-
-
# Execute all prepare callbacks.
-
1
def self.prepare!
-
1
new(nil).run_callbacks :prepare
-
end
-
-
# Execute all cleanup callbacks.
-
1
def self.cleanup!
-
new(nil).run_callbacks :cleanup
-
end
-
-
1
def initialize(app)
-
1
@app = app
-
end
-
-
1
module CleanupOnClose
-
1
def close
-
super if defined?(super)
-
ensure
-
ActionDispatch::Reloader.cleanup!
-
end
-
end
-
-
1
def call(env)
-
run_callbacks :prepare
-
response = @app.call(env)
-
response[2].extend(CleanupOnClose)
-
response
-
rescue Exception
-
run_callbacks :cleanup
-
raise
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
class RemoteIp
-
1
class IpSpoofAttackError < StandardError ; end
-
-
1
class RemoteIpGetter
-
1
def initialize(env, check_ip_spoofing, trusted_proxies)
-
@env = env
-
@check_ip_spoofing = check_ip_spoofing
-
@trusted_proxies = trusted_proxies
-
end
-
-
1
def remote_addrs
-
@remote_addrs ||= begin
-
list = @env['REMOTE_ADDR'] ? @env['REMOTE_ADDR'].split(/[,\s]+/) : []
-
list.reject { |addr| addr =~ @trusted_proxies }
-
end
-
end
-
-
1
def to_s
-
return remote_addrs.first if remote_addrs.any?
-
-
forwarded_ips = @env['HTTP_X_FORWARDED_FOR'] ? @env['HTTP_X_FORWARDED_FOR'].strip.split(/[,\s]+/) : []
-
-
if client_ip = @env['HTTP_CLIENT_IP']
-
if @check_ip_spoofing && !forwarded_ips.include?(client_ip)
-
# We don't know which came from the proxy, and which from the user
-
raise IpSpoofAttackError, "IP spoofing attack?!" \
-
"HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect}" \
-
"HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}"
-
end
-
return client_ip
-
end
-
-
return forwarded_ips.reject { |ip| ip =~ @trusted_proxies }.last || @env["REMOTE_ADDR"]
-
end
-
end
-
-
1
def initialize(app, check_ip_spoofing = true, trusted_proxies = nil)
-
1
@app = app
-
1
@check_ip_spoofing = check_ip_spoofing
-
1
regex = '(^127\.0\.0\.1$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\.)'
-
1
regex << "|(#{trusted_proxies})" if trusted_proxies
-
1
@trusted_proxies = Regexp.new(regex, "i")
-
end
-
-
1
def call(env)
-
env["action_dispatch.remote_ip"] = RemoteIpGetter.new(env, @check_ip_spoofing, @trusted_proxies)
-
@app.call(env)
-
end
-
end
-
end
-
1
require 'rack/utils'
-
1
require 'rack/request'
-
1
require 'rack/session/abstract/id'
-
1
require 'action_dispatch/middleware/cookies'
-
1
require 'active_support/core_ext/object/blank'
-
-
1
module ActionDispatch
-
1
module Session
-
1
class SessionRestoreError < StandardError #:nodoc:
-
end
-
-
1
module DestroyableSession
-
1
def destroy
-
clear
-
options = @env[Rack::Session::Abstract::ENV_SESSION_OPTIONS_KEY] if @env
-
options ||= {}
-
@by.send(:destroy_session, @env, options[:id], options) if @by
-
options[:id] = nil
-
@loaded = false
-
end
-
end
-
-
1
::Rack::Session::Abstract::SessionHash.send :include, DestroyableSession
-
-
1
module Compatibility
-
1
def initialize(app, options = {})
-
1
options[:key] ||= '_session_id'
-
1
super
-
end
-
-
1
def generate_sid
-
sid = SecureRandom.hex(16)
-
sid.encode!('UTF-8') if sid.respond_to?(:encode!)
-
sid
-
end
-
-
1
protected
-
-
1
def initialize_sid
-
1
@default_options.delete(:sidbits)
-
1
@default_options.delete(:secure_random)
-
end
-
end
-
-
1
module StaleSessionCheck
-
1
def load_session(env)
-
stale_session_check! { super }
-
end
-
-
1
def extract_session_id(env)
-
stale_session_check! { super }
-
end
-
-
1
def stale_session_check!
-
yield
-
rescue ArgumentError => argument_error
-
if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
-
begin
-
# Note that the regexp does not allow $1 to end with a ':'
-
$1.constantize
-
rescue LoadError, NameError => const_error
-
raise ActionDispatch::Session::SessionRestoreError, "Session contains objects whose class definition isn't available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: #{const_error.message} [#{const_error.class}])\n"
-
end
-
retry
-
else
-
raise
-
end
-
end
-
end
-
-
1
class AbstractStore < Rack::Session::Abstract::ID
-
1
include Compatibility
-
1
include StaleSessionCheck
-
-
1
def destroy_session(env, sid, options)
-
ActiveSupport::Deprecation.warn "Implementing #destroy in session stores is deprecated. " <<
-
"Please implement destroy_session(env, session_id, options) instead."
-
destroy(env)
-
end
-
-
1
def destroy(env)
-
raise '#destroy needs to be implemented.'
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'action_dispatch/middleware/session/abstract_store'
-
1
require 'rack/session/cookie'
-
-
1
module ActionDispatch
-
1
module Session
-
# This cookie-based session store is the Rails default. Sessions typically
-
# contain at most a user_id and flash message; both fit within the 4K cookie
-
# size limit. Cookie-based sessions are dramatically faster than the
-
# alternatives.
-
#
-
# If you have more than 4K of session data or don't want your data to be
-
# visible to the user, pick another session store.
-
#
-
# CookieOverflow is raised if you attempt to store more than 4K of data.
-
#
-
# A message digest is included with the cookie to ensure data integrity:
-
# a user cannot alter his +user_id+ without knowing the secret key
-
# included in the hash. New apps are generated with a pregenerated secret
-
# in config/environment.rb. Set your own for old apps you're upgrading.
-
#
-
# Session options:
-
#
-
# * <tt>:secret</tt>: An application-wide key string or block returning a
-
# string called per generated digest. The block is called with the
-
# CGI::Session instance as an argument. It's important that the secret
-
# is not vulnerable to a dictionary attack. Therefore, you should choose
-
# a secret consisting of random numbers and letters and more than 30
-
# characters. Examples:
-
#
-
# :secret => '449fe2e7daee471bffae2fd8dc02313d'
-
# :secret => Proc.new { User.current_user.secret_key }
-
#
-
# * <tt>:digest</tt>: The message digest algorithm used to verify session
-
# integrity defaults to 'SHA1' but may be any digest provided by OpenSSL,
-
# such as 'MD5', 'RIPEMD160', 'SHA256', etc.
-
#
-
# To generate a secret key for an existing application, run
-
# "rake secret" and set the key in config/initializers/secret_token.rb.
-
#
-
# Note that changing digest or secret invalidates all existing sessions!
-
1
class CookieStore < Rack::Session::Cookie
-
1
include Compatibility
-
1
include StaleSessionCheck
-
-
1
private
-
-
1
def unpacked_cookie_data(env)
-
env["action_dispatch.request.unsigned_session_cookie"] ||= begin
-
stale_session_check! do
-
request = ActionDispatch::Request.new(env)
-
if data = request.cookie_jar.signed[@key]
-
data.stringify_keys!
-
end
-
data || {}
-
end
-
end
-
end
-
-
1
def set_session(env, sid, session_data, options)
-
session_data.merge("session_id" => sid)
-
end
-
-
1
def set_cookie(env, session_id, cookie)
-
request = ActionDispatch::Request.new(env)
-
request.cookie_jar.signed[@key] = cookie
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/exception'
-
1
require 'active_support/notifications'
-
1
require 'action_dispatch/http/request'
-
-
1
module ActionDispatch
-
# This middleware rescues any exception returned by the application and renders
-
# nice exception pages if it's being rescued locally.
-
1
class ShowExceptions
-
1
RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates')
-
-
1
cattr_accessor :rescue_responses
-
1
@@rescue_responses = Hash.new(:internal_server_error)
-
1
@@rescue_responses.update({
-
'ActionController::RoutingError' => :not_found,
-
'AbstractController::ActionNotFound' => :not_found,
-
'ActiveRecord::RecordNotFound' => :not_found,
-
'ActiveRecord::StaleObjectError' => :conflict,
-
'ActiveRecord::RecordInvalid' => :unprocessable_entity,
-
'ActiveRecord::RecordNotSaved' => :unprocessable_entity,
-
'ActionController::MethodNotAllowed' => :method_not_allowed,
-
'ActionController::NotImplemented' => :not_implemented,
-
'ActionController::InvalidAuthenticityToken' => :unprocessable_entity
-
})
-
-
1
cattr_accessor :rescue_templates
-
1
@@rescue_templates = Hash.new('diagnostics')
-
1
@@rescue_templates.update({
-
'ActionView::MissingTemplate' => 'missing_template',
-
'ActionController::RoutingError' => 'routing_error',
-
'AbstractController::ActionNotFound' => 'unknown_action',
-
'ActionView::Template::Error' => 'template_error'
-
})
-
-
1
FAILSAFE_RESPONSE = [500, {'Content-Type' => 'text/html'},
-
["<html><body><h1>500 Internal Server Error</h1>" <<
-
"If you are the administrator of this website, then please read this web " <<
-
"application's log file and/or the web server's log file to find out what " <<
-
"went wrong.</body></html>"]]
-
-
1
def initialize(app, consider_all_requests_local = false)
-
1
@app = app
-
1
@consider_all_requests_local = consider_all_requests_local
-
end
-
-
1
def call(env)
-
begin
-
status, headers, body = @app.call(env)
-
exception = nil
-
-
# Only this middleware cares about RoutingError. So, let's just raise
-
# it here.
-
if headers['X-Cascade'] == 'pass'
-
raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
-
end
-
rescue Exception => exception
-
raise exception if env['action_dispatch.show_exceptions'] == false
-
end
-
-
exception ? render_exception(env, exception) : [status, headers, body]
-
end
-
-
1
private
-
1
def render_exception(env, exception)
-
log_error(exception)
-
exception = original_exception(exception)
-
-
request = Request.new(env)
-
if @consider_all_requests_local || request.local?
-
rescue_action_locally(request, exception)
-
else
-
rescue_action_in_public(exception)
-
end
-
rescue Exception => failsafe_error
-
$stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}"
-
FAILSAFE_RESPONSE
-
end
-
-
# Render detailed diagnostics for unhandled exceptions rescued from
-
# a controller action.
-
1
def rescue_action_locally(request, exception)
-
template = ActionView::Base.new([RESCUES_TEMPLATE_PATH],
-
:request => request,
-
:exception => exception,
-
:application_trace => application_trace(exception),
-
:framework_trace => framework_trace(exception),
-
:full_trace => full_trace(exception)
-
)
-
file = "rescues/#{@@rescue_templates[exception.class.name]}.erb"
-
body = template.render(:file => file, :layout => 'rescues/layout.erb')
-
render(status_code(exception), body)
-
end
-
-
# Attempts to render a static error page based on the
-
# <tt>status_code</tt> thrown, or just return headers if no such file
-
# exists. At first, it will try to render a localized static page.
-
# For example, if a 500 error is being handled Rails and locale is :da,
-
# it will first attempt to render the file at <tt>public/500.da.html</tt>
-
# then attempt to render <tt>public/500.html</tt>. If none of them exist,
-
# the body of the response will be left empty.
-
1
def rescue_action_in_public(exception)
-
status = status_code(exception)
-
locale_path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale
-
path = "#{public_path}/#{status}.html"
-
-
if locale_path && File.exist?(locale_path)
-
render(status, File.read(locale_path))
-
elsif File.exist?(path)
-
render(status, File.read(path))
-
else
-
render(status, '')
-
end
-
end
-
-
1
def status_code(exception)
-
Rack::Utils.status_code(@@rescue_responses[exception.class.name])
-
end
-
-
1
def render(status, body)
-
[status, {'Content-Type' => "text/html; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
-
end
-
-
1
def public_path
-
defined?(Rails.public_path) ? Rails.public_path : 'public_path'
-
end
-
-
1
def log_error(exception)
-
return unless logger
-
-
ActiveSupport::Deprecation.silence do
-
message = "\n#{exception.class} (#{exception.message}):\n"
-
message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
-
message << " " << application_trace(exception).join("\n ")
-
logger.fatal("#{message}\n\n")
-
end
-
end
-
-
1
def application_trace(exception)
-
clean_backtrace(exception, :silent)
-
end
-
-
1
def framework_trace(exception)
-
clean_backtrace(exception, :noise)
-
end
-
-
1
def full_trace(exception)
-
clean_backtrace(exception, :all)
-
end
-
-
1
def clean_backtrace(exception, *args)
-
defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) ?
-
Rails.backtrace_cleaner.clean(exception.backtrace, *args) :
-
exception.backtrace
-
end
-
-
1
def logger
-
defined?(Rails.logger) ? Rails.logger : Logger.new($stderr)
-
end
-
-
1
def original_exception(exception)
-
if registered_original_exception?(exception)
-
exception.original_exception
-
else
-
exception
-
end
-
end
-
-
1
def registered_original_exception?(exception)
-
exception.respond_to?(:original_exception) && @@rescue_responses.has_key?(exception.original_exception.class.name)
-
end
-
end
-
end
-
1
require "active_support/inflector/methods"
-
1
require "active_support/dependencies"
-
-
1
module ActionDispatch
-
1
class MiddlewareStack
-
1
class Middleware
-
1
attr_reader :args, :block, :name, :classcache
-
-
1
def initialize(klass_or_name, *args, &block)
-
20
@klass = nil
-
-
20
if klass_or_name.respond_to?(:name)
-
18
@klass = klass_or_name
-
18
@name = @klass.name
-
else
-
2
@name = klass_or_name.to_s
-
end
-
-
20
@classcache = ActiveSupport::Dependencies::Reference
-
20
@args, @block = args, block
-
end
-
-
1
def klass
-
20
@klass || classcache[@name]
-
end
-
-
1
def ==(middleware)
-
21
case middleware
-
when Middleware
-
klass == middleware.klass
-
when Class
-
klass == middleware
-
else
-
21
normalize(@name) == normalize(middleware)
-
end
-
end
-
-
1
def inspect
-
klass.to_s
-
end
-
-
1
def build(app)
-
20
klass.new(app, *args, &block)
-
end
-
-
1
private
-
-
1
def normalize(object)
-
42
object.to_s.strip.sub(/^::/, '')
-
end
-
end
-
-
1
include Enumerable
-
-
1
attr_accessor :middlewares
-
-
1
def initialize(*args)
-
2
@middlewares = []
-
2
yield(self) if block_given?
-
end
-
-
1
def each
-
@middlewares.each { |x| yield x }
-
end
-
-
1
def size
-
middlewares.size
-
end
-
-
1
def last
-
middlewares.last
-
end
-
-
1
def [](i)
-
middlewares[i]
-
end
-
-
1
def initialize_copy(other)
-
5
self.middlewares = other.middlewares.dup
-
end
-
-
1
def insert(index, *args, &block)
-
3
index = assert_index(index, :before)
-
3
middleware = self.class::Middleware.new(*args, &block)
-
3
middlewares.insert(index, middleware)
-
end
-
-
1
alias_method :insert_before, :insert
-
-
1
def insert_after(index, *args, &block)
-
2
index = assert_index(index, :after)
-
2
insert(index + 1, *args, &block)
-
end
-
-
1
def swap(target, *args, &block)
-
insert_before(target, *args, &block)
-
delete(target)
-
end
-
-
1
def delete(target)
-
middlewares.delete target
-
end
-
-
1
def use(*args, &block)
-
17
middleware = self.class::Middleware.new(*args, &block)
-
17
middlewares.push(middleware)
-
end
-
-
1
def build(app = nil, &block)
-
1
app ||= block
-
1
raise "MiddlewareStack#build requires an app" unless app
-
21
middlewares.reverse.inject(app) { |a, e| e.build(a) }
-
end
-
-
1
protected
-
-
1
def assert_index(index, where)
-
5
i = index.is_a?(Integer) ? index : middlewares.index(index)
-
5
raise "No such middleware to insert #{where}: #{index.inspect}" unless i
-
5
i
-
end
-
end
-
end
-
1
require 'rack/utils'
-
-
1
module ActionDispatch
-
1
class FileHandler
-
1
def initialize(root, cache_control)
-
1
@root = root.chomp('/')
-
1
@compiled_root = /^#{Regexp.escape(root)}/
-
1
@file_server = ::Rack::File.new(@root, cache_control)
-
end
-
-
1
def match?(path)
-
path = path.dup
-
-
full_path = path.empty? ? @root : File.join(@root, ::Rack::Utils.unescape(path))
-
paths = "#{full_path}#{ext}"
-
-
matches = Dir[paths]
-
match = matches.detect { |m| File.file?(m) }
-
if match
-
match.sub!(@compiled_root, '')
-
match
-
end
-
end
-
-
1
def call(env)
-
@file_server.call(env)
-
end
-
-
1
def ext
-
@ext ||= begin
-
ext = ::ActionController::Base.page_cache_extension
-
"{,#{ext},/index#{ext}}"
-
end
-
end
-
end
-
-
1
class Static
-
1
def initialize(app, path, cache_control=nil)
-
1
@app = app
-
1
@file_handler = FileHandler.new(path, cache_control)
-
end
-
-
1
def call(env)
-
case env['REQUEST_METHOD']
-
when 'GET', 'HEAD'
-
path = env['PATH_INFO'].chomp('/')
-
if match = @file_handler.match?(path)
-
env["PATH_INFO"] = match
-
return @file_handler.call(env)
-
end
-
end
-
-
@app.call(env)
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/to_param'
-
1
require 'active_support/core_ext/regexp'
-
-
1
module ActionDispatch
-
# The routing module provides URL rewriting in native Ruby. It's a way to
-
# redirect incoming requests to controllers and actions. This replaces
-
# mod_rewrite rules. Best of all, Rails' \Routing works with any web server.
-
# Routes are defined in <tt>config/routes.rb</tt>.
-
#
-
# Think of creating routes as drawing a map for your requests. The map tells
-
# them where to go based on some predefined pattern:
-
#
-
# AppName::Application.routes.draw do
-
# Pattern 1 tells some request to go to one place
-
# Pattern 2 tell them to go to another
-
# ...
-
# end
-
#
-
# The following symbols are special:
-
#
-
# :controller maps to your controller name
-
# :action maps to an action with your controllers
-
#
-
# Other names simply map to a parameter as in the case of <tt>:id</tt>.
-
#
-
# == Resources
-
#
-
# Resource routing allows you to quickly declare all of the common routes
-
# for a given resourceful controller. Instead of declaring separate routes
-
# for your +index+, +show+, +new+, +edit+, +create+, +update+ and +destroy+
-
# actions, a resourceful route declares them in a single line of code:
-
#
-
# resources :photos
-
#
-
# Sometimes, you have a resource that clients always look up without
-
# referencing an ID. A common example, /profile always shows the profile of
-
# the currently logged in user. In this case, you can use a singular resource
-
# to map /profile (rather than /profile/:id) to the show action.
-
#
-
# resource :profile
-
#
-
# It's common to have resources that are logically children of other
-
# resources:
-
#
-
# resources :magazines do
-
# resources :ads
-
# end
-
#
-
# You may wish to organize groups of controllers under a namespace. Most
-
# commonly, you might group a number of administrative controllers under
-
# an +admin+ namespace. You would place these controllers under the
-
# <tt>app/controllers/admin</tt> directory, and you can group them together
-
# in your router:
-
#
-
# namespace "admin" do
-
# resources :posts, :comments
-
# end
-
#
-
# Alternately, you can add prefixes to your path without using a separate
-
# directory by using +scope+. +scope+ takes additional options which
-
# apply to all enclosed routes.
-
#
-
# scope :path => "/cpanel", :as => 'admin' do
-
# resources :posts, :comments
-
# end
-
#
-
# For more, see <tt>Routing::Mapper::Resources#resources</tt>,
-
# <tt>Routing::Mapper::Scoping#namespace</tt>, and
-
# <tt>Routing::Mapper::Scoping#scope</tt>.
-
#
-
# == Named routes
-
#
-
# Routes can be named by passing an <tt>:as</tt> option,
-
# allowing for easy reference within your source as +name_of_route_url+
-
# for the full URL and +name_of_route_path+ for the URI path.
-
#
-
# Example:
-
#
-
# # In routes.rb
-
# match '/login' => 'accounts#login', :as => 'login'
-
#
-
# # With render, redirect_to, tests, etc.
-
# redirect_to login_url
-
#
-
# Arguments can be passed as well.
-
#
-
# redirect_to show_item_path(:id => 25)
-
#
-
# Use <tt>root</tt> as a shorthand to name a route for the root path "/".
-
#
-
# # In routes.rb
-
# root :to => 'blogs#index'
-
#
-
# # would recognize http://www.example.com/ as
-
# params = { :controller => 'blogs', :action => 'index' }
-
#
-
# # and provide these named routes
-
# root_url # => 'http://www.example.com/'
-
# root_path # => '/'
-
#
-
# Note: when using +controller+, the route is simply named after the
-
# method you call on the block parameter rather than map.
-
#
-
# # In routes.rb
-
# controller :blog do
-
# match 'blog/show' => :list
-
# match 'blog/delete' => :delete
-
# match 'blog/edit/:id' => :edit
-
# end
-
#
-
# # provides named routes for show, delete, and edit
-
# link_to @article.title, show_path(:id => @article.id)
-
#
-
# == Pretty URLs
-
#
-
# Routes can generate pretty URLs. For example:
-
#
-
# match '/articles/:year/:month/:day' => 'articles#find_by_id', :constraints => {
-
# :year => /\d{4}/,
-
# :month => /\d{1,2}/,
-
# :day => /\d{1,2}/
-
# }
-
#
-
# Using the route above, the URL "http://localhost:3000/articles/2005/11/06"
-
# maps to
-
#
-
# params = {:year => '2005', :month => '11', :day => '06'}
-
#
-
# == Regular Expressions and parameters
-
# You can specify a regular expression to define a format for a parameter.
-
#
-
# controller 'geocode' do
-
# match 'geocode/:postalcode' => :show, :constraints => {
-
# :postalcode => /\d{5}(-\d{4})?/
-
# }
-
#
-
# Constraints can include the 'ignorecase' and 'extended syntax' regular
-
# expression modifiers:
-
#
-
# controller 'geocode' do
-
# match 'geocode/:postalcode' => :show, :constraints => {
-
# :postalcode => /hx\d\d\s\d[a-z]{2}/i
-
# }
-
# end
-
#
-
# controller 'geocode' do
-
# match 'geocode/:postalcode' => :show, :constraints => {
-
# :postalcode => /# Postcode format
-
# \d{5} #Prefix
-
# (-\d{4})? #Suffix
-
# /x
-
# }
-
# end
-
#
-
# Using the multiline match modifier will raise an +ArgumentError+.
-
# Encoding regular expression modifiers are silently ignored. The
-
# match will always use the default encoding or ASCII.
-
#
-
# == Default route
-
#
-
# Consider the following route, which you will find commented out at the
-
# bottom of your generated <tt>config/routes.rb</tt>:
-
#
-
# match ':controller(/:action(/:id(.:format)))'
-
#
-
# This route states that it expects requests to consist of a
-
# <tt>:controller</tt> followed optionally by an <tt>:action</tt> that in
-
# turn is followed optionally by an <tt>:id</tt>, which in turn is followed
-
# optionally by a <tt>:format</tt>.
-
#
-
# Suppose you get an incoming request for <tt>/blog/edit/22</tt>, you'll end
-
# up with:
-
#
-
# params = { :controller => 'blog',
-
# :action => 'edit',
-
# :id => '22'
-
# }
-
#
-
# By not relying on default routes, you improve the security of your
-
# application since not all controller actions, which includes actions you
-
# might add at a later time, are exposed by default.
-
#
-
# == HTTP Methods
-
#
-
# Using the <tt>:via</tt> option when specifying a route allows you to restrict it to a specific HTTP method.
-
# Possible values are <tt>:post</tt>, <tt>:get</tt>, <tt>:put</tt>, <tt>:delete</tt> and <tt>:any</tt>.
-
# If your route needs to respond to more than one method you can use an array, e.g. <tt>[ :get, :post ]</tt>.
-
# The default value is <tt>:any</tt> which means that the route will respond to any of the HTTP methods.
-
#
-
# Examples:
-
#
-
# match 'post/:id' => 'posts#show', :via => :get
-
# match 'post/:id' => "posts#create_comment', :via => :post
-
#
-
# Now, if you POST to <tt>/posts/:id</tt>, it will route to the <tt>create_comment</tt> action. A GET on the same
-
# URL will route to the <tt>show</tt> action.
-
#
-
# === HTTP helper methods
-
#
-
# An alternative method of specifying which HTTP method a route should respond to is to use the helper
-
# methods <tt>get</tt>, <tt>post</tt>, <tt>put</tt> and <tt>delete</tt>.
-
#
-
# Examples:
-
#
-
# get 'post/:id' => 'posts#show'
-
# post 'post/:id' => "posts#create_comment'
-
#
-
# This syntax is less verbose and the intention is more apparent to someone else reading your code,
-
# however if your route needs to respond to more than one HTTP method (or all methods) then using the
-
# <tt>:via</tt> option on <tt>match</tt> is preferable.
-
#
-
# == External redirects
-
#
-
# You can redirect any path to another path using the redirect helper in your router:
-
#
-
# match "/stories" => redirect("/posts")
-
#
-
# == Routing to Rack Applications
-
#
-
# Instead of a String, like <tt>posts#index</tt>, which corresponds to the
-
# index action in the PostsController, you can specify any Rack application
-
# as the endpoint for a matcher:
-
#
-
# match "/application.js" => Sprockets
-
#
-
# == Reloading routes
-
#
-
# You can reload routes if you feel you must:
-
#
-
# Rails.application.reload_routes!
-
#
-
# This will clear all named routes and reload routes.rb if the file has been modified from
-
# last load. To absolutely force reloading, use <tt>reload!</tt>.
-
#
-
# == Testing Routes
-
#
-
# The two main methods for testing your routes:
-
#
-
# === +assert_routing+
-
#
-
# def test_movie_route_properly_splits
-
# opts = {:controller => "plugin", :action => "checkout", :id => "2"}
-
# assert_routing "plugin/checkout/2", opts
-
# end
-
#
-
# +assert_routing+ lets you test whether or not the route properly resolves into options.
-
#
-
# === +assert_recognizes+
-
#
-
# def test_route_has_options
-
# opts = {:controller => "plugin", :action => "show", :id => "12"}
-
# assert_recognizes opts, "/plugins/show/12"
-
# end
-
#
-
# Note the subtle difference between the two: +assert_routing+ tests that
-
# a URL fits options while +assert_recognizes+ tests that a URL
-
# breaks into parameters properly.
-
#
-
# In tests you can simply pass the URL or named route to +get+ or +post+.
-
#
-
# def send_to_jail
-
# get '/jail'
-
# assert_response :success
-
# assert_template "jail/front"
-
# end
-
#
-
# def goes_to_login
-
# get login_url
-
# #...
-
# end
-
#
-
# == View a list of all your routes
-
#
-
# rake routes
-
#
-
# Target specific controllers by prefixing the command with <tt>CONTROLLER=x</tt>.
-
#
-
1
module Routing
-
1
autoload :Mapper, 'action_dispatch/routing/mapper'
-
1
autoload :Route, 'action_dispatch/routing/route'
-
1
autoload :RouteSet, 'action_dispatch/routing/route_set'
-
1
autoload :RoutesProxy, 'action_dispatch/routing/routes_proxy'
-
1
autoload :UrlFor, 'action_dispatch/routing/url_for'
-
1
autoload :PolymorphicRoutes, 'action_dispatch/routing/polymorphic_routes'
-
-
1
SEPARATORS = %w( / . ? ) #:nodoc:
-
1
HTTP_METHODS = [:get, :head, :post, :put, :delete, :options] #:nodoc:
-
-
# A helper module to hold URL related helpers.
-
1
module Helpers #:nodoc:
-
1
include PolymorphicRoutes
-
end
-
end
-
end
-
1
require 'erb'
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/object/inclusion'
-
1
require 'active_support/inflector'
-
1
require 'action_dispatch/routing/redirection'
-
-
1
module ActionDispatch
-
1
module Routing
-
1
class Mapper
-
1
class Constraints #:nodoc:
-
1
def self.new(app, constraints, request = Rack::Request)
-
23
if constraints.any?
-
super(app, constraints, request)
-
else
-
23
app
-
end
-
end
-
-
1
attr_reader :app
-
-
1
def initialize(app, constraints, request)
-
@app, @constraints, @request = app, constraints, request
-
end
-
-
1
def matches?(env)
-
req = @request.new(env)
-
-
@constraints.each { |constraint|
-
if constraint.respond_to?(:matches?) && !constraint.matches?(req)
-
return false
-
elsif constraint.respond_to?(:call) && !constraint.call(*constraint_args(constraint, req))
-
return false
-
end
-
}
-
-
return true
-
end
-
-
1
def call(env)
-
matches?(env) ? @app.call(env) : [ 404, {'X-Cascade' => 'pass'}, [] ]
-
end
-
-
1
private
-
1
def constraint_args(constraint, request)
-
constraint.arity == 1 ? [request] : [request.symbolized_path_parameters, request]
-
end
-
end
-
-
1
class Mapping #:nodoc:
-
1
IGNORE_OPTIONS = [:to, :as, :via, :on, :constraints, :defaults, :only, :except, :anchor, :shallow, :shallow_path, :shallow_prefix]
-
-
1
def initialize(set, scope, path, options)
-
23
@set, @scope = set, scope
-
23
@options = (@scope[:options] || {}).merge(options)
-
23
@path = normalize_path(path)
-
23
normalize_options!
-
end
-
-
1
def to_route
-
23
[ app, conditions, requirements, defaults, @options[:as], @options[:anchor] ]
-
end
-
-
1
private
-
-
1
def normalize_options!
-
23
path_without_format = @path.sub(/\(\.:format\)$/, '')
-
-
23
if using_match_shorthand?(path_without_format, @options)
-
1
to_shorthand = @options[:to].blank?
-
1
@options[:to] ||= path_without_format[1..-1].sub(%r{/([^/]*)$}, '#\1')
-
end
-
-
23
@options.merge!(default_controller_and_action(to_shorthand))
-
-
23
requirements.each do |name, requirement|
-
# segment_keys.include?(k.to_s) || k == :controller
-
next unless Regexp === requirement && !constraints[name]
-
-
if requirement.source =~ %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
-
raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
-
end
-
if requirement.multiline?
-
raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}"
-
end
-
end
-
end
-
-
# match "account/overview"
-
1
def using_match_shorthand?(path, options)
-
23
path && options.except(:via, :anchor, :to, :as).empty? && path =~ %r{^/[\w\/]+$}
-
end
-
-
1
def normalize_path(path)
-
23
raise ArgumentError, "path is required" if path.blank?
-
23
path = Mapper.normalize_path(path)
-
-
23
if path.match(':controller')
-
raise ArgumentError, ":controller segment is not allowed within a namespace block" if @scope[:module]
-
-
# Add a default constraint for :controller path segments that matches namespaced
-
# controllers with default routes like :controller/:action/:id(.:format), e.g:
-
# GET /admin/products/show/1
-
# => { :controller => 'admin/products', :action => 'show', :id => '1' }
-
@options.reverse_merge!(:controller => /.+?/)
-
end
-
-
# Add a constraint for wildcard route to make it non-greedy and match the
-
# optional format part of the route by default
-
23
if path.match(/\*([^\/]+)$/) && @options[:format] != false
-
@options.reverse_merge!(:"#{$1}" => /.+?/)
-
end
-
-
23
if @options[:format] == false
-
1
@options.delete(:format)
-
1
path
-
22
elsif path.include?(":format") || path.end_with?('/')
-
1
path
-
21
elsif @options[:format] == true
-
"#{path}.:format"
-
else
-
21
"#{path}(.:format)"
-
end
-
end
-
-
1
def app
-
23
Constraints.new(
-
to.respond_to?(:call) ? to : Routing::RouteSet::Dispatcher.new(:defaults => defaults),
-
blocks,
-
@set.request_class
-
)
-
end
-
-
1
def conditions
-
23
{ :path_info => @path }.merge(constraints).merge(request_method_condition)
-
end
-
-
1
def requirements
-
23
@requirements ||= (@options[:constraints].is_a?(Hash) ? @options[:constraints] : {}).tap do |requirements|
-
23
requirements.reverse_merge!(@scope[:constraints]) if @scope[:constraints]
-
142
@options.each { |k, v| requirements[k] = v if v.is_a?(Regexp) }
-
69
end
-
end
-
-
1
def defaults
-
23
@defaults ||= (@options[:defaults] || {}).tap do |defaults|
-
23
defaults.reverse_merge!(@scope[:defaults]) if @scope[:defaults]
-
142
@options.each { |k, v| defaults[k] = v unless v.is_a?(Regexp) || IGNORE_OPTIONS.include?(k.to_sym) }
-
45
end
-
end
-
-
1
def default_controller_and_action(to_shorthand=nil)
-
23
if to.respond_to?(:call)
-
1
{ }
-
else
-
22
if to.is_a?(String)
-
8
controller, action = to.split('#')
-
elsif to.is_a?(Symbol)
-
action = to.to_s
-
end
-
-
22
controller ||= default_controller
-
22
action ||= default_action
-
-
22
unless controller.is_a?(Regexp) || to_shorthand
-
22
controller = [@scope[:module], controller].compact.join("/").presence
-
end
-
-
22
if controller.is_a?(String) && controller =~ %r{\A/}
-
raise ArgumentError, "controller name should not start with a slash"
-
end
-
-
22
controller = controller.to_s unless controller.is_a?(Regexp)
-
22
action = action.to_s unless action.is_a?(Regexp)
-
-
22
if controller.blank? && segment_keys.exclude?("controller")
-
raise ArgumentError, "missing :controller"
-
end
-
-
22
if action.blank? && segment_keys.exclude?("action")
-
raise ArgumentError, "missing :action"
-
end
-
-
22
hash = {}
-
22
hash[:controller] = controller unless controller.blank?
-
22
hash[:action] = action unless action.blank?
-
22
hash
-
end
-
end
-
-
1
def blocks
-
23
constraints = @options[:constraints]
-
23
if constraints.present? && !constraints.is_a?(Hash)
-
[constraints]
-
else
-
23
@scope[:blocks] || []
-
end
-
end
-
-
1
def constraints
-
23
@constraints ||= requirements.reject { |k, v| segment_keys.include?(k.to_s) || k == :controller }
-
end
-
-
1
def request_method_condition
-
23
if via = @options[:via]
-
42
list = Array(via).map { |m| m.to_s.dasherize.upcase }
-
21
{ :request_method => list }
-
else
-
2
{ }
-
end
-
end
-
-
1
def segment_keys
-
@segment_keys ||= Rack::Mount::RegexpWithNamedGroups.new(
-
Rack::Mount::Strexp.compile(@path, requirements, SEPARATORS)
-
).names
-
end
-
-
1
def to
-
91
@options[:to]
-
end
-
-
1
def default_controller
-
14
if @options[:controller]
-
@options[:controller]
-
14
elsif @scope[:controller]
-
14
@scope[:controller]
-
end
-
end
-
-
1
def default_action
-
14
if @options[:action]
-
14
@options[:action]
-
elsif @scope[:action]
-
@scope[:action]
-
end
-
end
-
end
-
-
# Invokes Rack::Mount::Utils.normalize path and ensure that
-
# (:locale) becomes (/:locale) instead of /(:locale). Except
-
# for root cases, where the latter is the correct one.
-
1
def self.normalize_path(path)
-
39
path = Rack::Mount::Utils.normalize_path(path)
-
39
path.gsub!(%r{/(\(+)/?}, '\1/') unless path =~ %r{^/\(+[^/]+\)$}
-
39
path
-
end
-
-
1
def self.normalize_name(name)
-
10
normalize_path(name)[1..-1].gsub("/", "_")
-
end
-
-
1
module Base
-
# You can specify what Rails should route "/" to with the root method:
-
#
-
# root :to => 'pages#main'
-
#
-
# For options, see +match+, as +root+ uses it internally.
-
#
-
# You should put the root route at the top of <tt>config/routes.rb</tt>,
-
# because this means it will be matched first. As this is the most popular route
-
# of most Rails applications, this is beneficial.
-
1
def root(options = {})
-
1
match '/', options.reverse_merge(:as => :root)
-
end
-
-
# Matches a url pattern to one or more routes. Any symbols in a pattern
-
# are interpreted as url query parameters and thus available as +params+
-
# in an action:
-
#
-
# # sets :controller, :action and :id in params
-
# match ':controller/:action/:id'
-
#
-
# Two of these symbols are special, +:controller+ maps to the controller
-
# and +:action+ to the controller's action. A pattern can also map
-
# wildcard segments (globs) to params:
-
#
-
# match 'songs/*category/:title' => 'songs#show'
-
#
-
# # 'songs/rock/classic/stairway-to-heaven' sets
-
# # params[:category] = 'rock/classic'
-
# # params[:title] = 'stairway-to-heaven'
-
#
-
# When a pattern points to an internal route, the route's +:action+ and
-
# +:controller+ should be set in options or hash shorthand. Examples:
-
#
-
# match 'photos/:id' => 'photos#show'
-
# match 'photos/:id', :to => 'photos#show'
-
# match 'photos/:id', :controller => 'photos', :action => 'show'
-
#
-
# A pattern can also point to a +Rack+ endpoint i.e. anything that
-
# responds to +call+:
-
#
-
# match 'photos/:id' => lambda {|hash| [200, {}, "Coming soon" }
-
# match 'photos/:id' => PhotoRackApp
-
# # Yes, controller actions are just rack endpoints
-
# match 'photos/:id' => PhotosController.action(:show)
-
#
-
# === Options
-
#
-
# Any options not seen here are passed on as params with the url.
-
#
-
# [:controller]
-
# The route's controller.
-
#
-
# [:action]
-
# The route's action.
-
#
-
# [:path]
-
# The path prefix for the routes.
-
#
-
# [:module]
-
# The namespace for :controller.
-
#
-
# match 'path' => 'c#a', :module => 'sekret', :controller => 'posts'
-
# #=> Sekret::PostsController
-
#
-
# See <tt>Scoping#namespace</tt> for its scope equivalent.
-
#
-
# [:as]
-
# The name used to generate routing helpers.
-
#
-
# [:via]
-
# Allowed HTTP verb(s) for route.
-
#
-
# match 'path' => 'c#a', :via => :get
-
# match 'path' => 'c#a', :via => [:get, :post]
-
#
-
# [:to]
-
# Points to a +Rack+ endpoint. Can be an object that responds to
-
# +call+ or a string representing a controller's action.
-
#
-
# match 'path', :to => 'controller#action'
-
# match 'path', :to => lambda { [200, {}, "Success!"] }
-
# match 'path', :to => RackApp
-
#
-
# [:on]
-
# Shorthand for wrapping routes in a specific RESTful context. Valid
-
# values are +:member+, +:collection+, and +:new+. Only use within
-
# <tt>resource(s)</tt> block. For example:
-
#
-
# resource :bar do
-
# match 'foo' => 'c#a', :on => :member, :via => [:get, :post]
-
# end
-
#
-
# Is equivalent to:
-
#
-
# resource :bar do
-
# member do
-
# match 'foo' => 'c#a', :via => [:get, :post]
-
# end
-
# end
-
#
-
# [:constraints]
-
# Constrains parameters with a hash of regular expressions or an
-
# object that responds to <tt>matches?</tt>
-
#
-
# match 'path/:id', :constraints => { :id => /[A-Z]\d{5}/ }
-
#
-
# class Blacklist
-
# def matches?(request) request.remote_ip == '1.2.3.4' end
-
# end
-
# match 'path' => 'c#a', :constraints => Blacklist.new
-
#
-
# See <tt>Scoping#constraints</tt> for more examples with its scope
-
# equivalent.
-
#
-
# [:defaults]
-
# Sets defaults for parameters
-
#
-
# # Sets params[:format] to 'jpg' by default
-
# match 'path' => 'c#a', :defaults => { :format => 'jpg' }
-
#
-
# See <tt>Scoping#defaults</tt> for its scope equivalent.
-
#
-
# [:anchor]
-
# Boolean to anchor a <tt>match</tt> pattern. Default is true. When set to
-
# false, the pattern matches any request prefixed with the given path.
-
#
-
# # Matches any request starting with 'path'
-
# match 'path' => 'c#a', :anchor => false
-
1
def match(path, options=nil)
-
23
mapping = Mapping.new(@set, @scope, path, options || {})
-
23
app, conditions, requirements, defaults, as, anchor = mapping.to_route
-
23
@set.add_route(app, conditions, requirements, defaults, as, anchor)
-
23
self
-
end
-
-
# Mount a Rack-based application to be used within the application.
-
#
-
# mount SomeRackApp, :at => "some_route"
-
#
-
# Alternatively:
-
#
-
# mount(SomeRackApp => "some_route")
-
#
-
# For options, see +match+, as +mount+ uses it internally.
-
#
-
# All mounted applications come with routing helpers to access them.
-
# These are named after the class specified, so for the above example
-
# the helper is either +some_rack_app_path+ or +some_rack_app_url+.
-
# To customize this helper's name, use the +:as+ option:
-
#
-
# mount(SomeRackApp => "some_route", :as => "exciting")
-
#
-
# This will generate the +exciting_path+ and +exciting_url+ helpers
-
# which can be used to navigate to this mounted app.
-
1
def mount(app, options = nil)
-
1
if options
-
path = options.delete(:at)
-
else
-
1
options = app
-
2
app, path = options.find { |k, v| k.respond_to?(:call) }
-
1
options.delete(app) if app
-
end
-
-
1
raise "A rack application must be specified" unless path
-
-
1
options[:as] ||= app_name(app)
-
-
1
match(path, options.merge(:to => app, :anchor => false, :format => false))
-
-
1
define_generate_prefix(app, options[:as])
-
1
self
-
end
-
-
1
def default_url_options=(options)
-
@set.default_url_options = options
-
end
-
1
alias_method :default_url_options, :default_url_options=
-
-
1
def with_default_scope(scope, &block)
-
scope(scope) do
-
instance_exec(&block)
-
end
-
end
-
-
1
private
-
1
def app_name(app)
-
1
return unless app.respond_to?(:routes)
-
-
if app.respond_to?(:railtie_name)
-
app.railtie_name
-
else
-
class_name = app.class.is_a?(Class) ? app.name : app.class.name
-
ActiveSupport::Inflector.underscore(class_name).gsub("/", "_")
-
end
-
end
-
-
1
def define_generate_prefix(app, name)
-
1
return unless app.respond_to?(:routes) && app.routes.respond_to?(:define_mounted_helper)
-
-
_route = @set.named_routes.routes[name.to_sym]
-
_routes = @set
-
app.routes.define_mounted_helper(name)
-
app.routes.class_eval do
-
define_method :_generate_prefix do |options|
-
prefix_options = options.slice(*_route.segment_keys)
-
# we must actually delete prefix segment keys to avoid passing them to next url_for
-
_route.segment_keys.each { |k| options.delete(k) }
-
_routes.url_helpers.send("#{name}_path", prefix_options)
-
end
-
end
-
end
-
end
-
-
1
module HttpHelpers
-
# Define a route that only recognizes HTTP GET.
-
# For supported arguments, see <tt>Base#match</tt>.
-
#
-
# Example:
-
#
-
# get 'bacon', :to => 'food#bacon'
-
1
def get(*args, &block)
-
8
map_method(:get, *args, &block)
-
end
-
-
# Define a route that only recognizes HTTP POST.
-
# For supported arguments, see <tt>Base#match</tt>.
-
#
-
# Example:
-
#
-
# post 'bacon', :to => 'food#bacon'
-
1
def post(*args, &block)
-
2
map_method(:post, *args, &block)
-
end
-
-
# Define a route that only recognizes HTTP PUT.
-
# For supported arguments, see <tt>Base#match</tt>.
-
#
-
# Example:
-
#
-
# put 'bacon', :to => 'food#bacon'
-
1
def put(*args, &block)
-
2
map_method(:put, *args, &block)
-
end
-
-
# Define a route that only recognizes HTTP PUT.
-
# For supported arguments, see <tt>Base#match</tt>.
-
#
-
# Example:
-
#
-
# delete 'broccoli', :to => 'food#broccoli'
-
1
def delete(*args, &block)
-
2
map_method(:delete, *args, &block)
-
end
-
-
1
private
-
1
def map_method(method, *args, &block)
-
14
options = args.extract_options!
-
14
options[:via] = method
-
14
args.push(options)
-
14
match(*args, &block)
-
14
self
-
end
-
end
-
-
# You may wish to organize groups of controllers under a namespace.
-
# Most commonly, you might group a number of administrative controllers
-
# under an +admin+ namespace. You would place these controllers under
-
# the <tt>app/controllers/admin</tt> directory, and you can group them
-
# together in your router:
-
#
-
# namespace "admin" do
-
# resources :posts, :comments
-
# end
-
#
-
# This will create a number of routes for each of the posts and comments
-
# controller. For <tt>Admin::PostsController</tt>, Rails will create:
-
#
-
# GET /admin/posts
-
# GET /admin/posts/new
-
# POST /admin/posts
-
# GET /admin/posts/1
-
# GET /admin/posts/1/edit
-
# PUT /admin/posts/1
-
# DELETE /admin/posts/1
-
#
-
# If you want to route /posts (without the prefix /admin) to
-
# <tt>Admin::PostsController</tt>, you could use
-
#
-
# scope :module => "admin" do
-
# resources :posts
-
# end
-
#
-
# or, for a single case
-
#
-
# resources :posts, :module => "admin"
-
#
-
# If you want to route /admin/posts to +PostsController+
-
# (without the Admin:: module prefix), you could use
-
#
-
# scope "/admin" do
-
# resources :posts
-
# end
-
#
-
# or, for a single case
-
#
-
# resources :posts, :path => "/admin/posts"
-
#
-
# In each of these cases, the named routes remain the same as if you did
-
# not use scope. In the last case, the following paths map to
-
# +PostsController+:
-
#
-
# GET /admin/posts
-
# GET /admin/posts/new
-
# POST /admin/posts
-
# GET /admin/posts/1
-
# GET /admin/posts/1/edit
-
# PUT /admin/posts/1
-
# DELETE /admin/posts/1
-
1
module Scoping
-
# Scopes a set of routes to the given default options.
-
#
-
# Take the following route definition as an example:
-
#
-
# scope :path => ":account_id", :as => "account" do
-
# resources :projects
-
# end
-
#
-
# This generates helpers such as +account_projects_path+, just like +resources+ does.
-
# The difference here being that the routes generated are like /rails/projects/2,
-
# rather than /accounts/rails/projects/2.
-
#
-
# === Options
-
#
-
# Takes same options as <tt>Base#match</tt> and <tt>Resources#resources</tt>.
-
#
-
# === Examples
-
#
-
# # route /posts (without the prefix /admin) to <tt>Admin::PostsController</tt>
-
# scope :module => "admin" do
-
# resources :posts
-
# end
-
#
-
# # prefix the posts resource's requests with '/admin'
-
# scope :path => "/admin" do
-
# resources :posts
-
# end
-
#
-
# # prefix the routing helper name: +sekret_posts_path+ instead of +posts_path+
-
# scope :as => "sekret" do
-
# resources :posts
-
# end
-
1
def scope(*args)
-
8
options = args.extract_options!
-
8
options = options.dup
-
-
8
options[:path] = args.first if args.first.is_a?(String)
-
8
recover = {}
-
-
8
options[:constraints] ||= {}
-
8
unless options[:constraints].is_a?(Hash)
-
block, options[:constraints] = options[:constraints], {}
-
end
-
-
8
scope_options.each do |option|
-
96
if value = options.delete(option)
-
16
recover[option] = @scope[option]
-
16
@scope[option] = send("merge_#{option}_scope", @scope[option], value)
-
end
-
end
-
-
8
recover[:block] = @scope[:blocks]
-
8
@scope[:blocks] = merge_blocks_scope(@scope[:blocks], block)
-
-
8
recover[:options] = @scope[:options]
-
8
@scope[:options] = merge_options_scope(@scope[:options], options)
-
-
8
yield
-
8
self
-
ensure
-
8
scope_options.each do |option|
-
96
@scope[option] = recover[option] if recover.has_key?(option)
-
end
-
-
8
@scope[:options] = recover[:options]
-
8
@scope[:blocks] = recover[:block]
-
end
-
-
# Scopes routes to a specific controller
-
#
-
# Example:
-
# controller "food" do
-
# match "bacon", :action => "bacon"
-
# end
-
1
def controller(controller, options={})
-
options[:controller] = controller
-
scope(options) { yield }
-
end
-
-
# Scopes routes to a specific namespace. For example:
-
#
-
# namespace :admin do
-
# resources :posts
-
# end
-
#
-
# This generates the following routes:
-
#
-
# admin_posts GET /admin/posts(.:format) {:action=>"index", :controller=>"admin/posts"}
-
# admin_posts POST /admin/posts(.:format) {:action=>"create", :controller=>"admin/posts"}
-
# new_admin_post GET /admin/posts/new(.:format) {:action=>"new", :controller=>"admin/posts"}
-
# edit_admin_post GET /admin/posts/:id/edit(.:format) {:action=>"edit", :controller=>"admin/posts"}
-
# admin_post GET /admin/posts/:id(.:format) {:action=>"show", :controller=>"admin/posts"}
-
# admin_post PUT /admin/posts/:id(.:format) {:action=>"update", :controller=>"admin/posts"}
-
# admin_post DELETE /admin/posts/:id(.:format) {:action=>"destroy", :controller=>"admin/posts"}
-
#
-
# === Options
-
#
-
# The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+
-
# options all default to the name of the namespace.
-
#
-
# For options, see <tt>Base#match</tt>. For +:shallow_path+ option, see
-
# <tt>Resources#resources</tt>.
-
#
-
# === Examples
-
#
-
# # accessible through /sekret/posts rather than /admin/posts
-
# namespace :admin, :path => "sekret" do
-
# resources :posts
-
# end
-
#
-
# # maps to <tt>Sekret::PostsController</tt> rather than <tt>Admin::PostsController</tt>
-
# namespace :admin, :module => "sekret" do
-
# resources :posts
-
# end
-
#
-
# # generates +sekret_posts_path+ rather than +admin_posts_path+
-
# namespace :admin, :as => "sekret" do
-
# resources :posts
-
# end
-
1
def namespace(path, options = {})
-
path = path.to_s
-
options = { :path => path, :as => path, :module => path,
-
:shallow_path => path, :shallow_prefix => path }.merge!(options)
-
scope(options) { yield }
-
end
-
-
# === Parameter Restriction
-
# Allows you to constrain the nested routes based on a set of rules.
-
# For instance, in order to change the routes to allow for a dot character in the +id+ parameter:
-
#
-
# constraints(:id => /\d+\.\d+) do
-
# resources :posts
-
# end
-
#
-
# Now routes such as +/posts/1+ will no longer be valid, but +/posts/1.1+ will be.
-
# The +id+ parameter must match the constraint passed in for this example.
-
#
-
# You may use this to also restrict other parameters:
-
#
-
# resources :posts do
-
# constraints(:post_id => /\d+\.\d+) do
-
# resources :comments
-
# end
-
# end
-
#
-
# === Restricting based on IP
-
#
-
# Routes can also be constrained to an IP or a certain range of IP addresses:
-
#
-
# constraints(:ip => /192.168.\d+.\d+/) do
-
# resources :posts
-
# end
-
#
-
# Any user connecting from the 192.168.* range will be able to see this resource,
-
# where as any user connecting outside of this range will be told there is no such route.
-
#
-
# === Dynamic request matching
-
#
-
# Requests to routes can be constrained based on specific criteria:
-
#
-
# constraints(lambda { |req| req.env["HTTP_USER_AGENT"] =~ /iPhone/ }) do
-
# resources :iphones
-
# end
-
#
-
# You are able to move this logic out into a class if it is too complex for routes.
-
# This class must have a +matches?+ method defined on it which either returns +true+
-
# if the user should be given access to that route, or +false+ if the user should not.
-
#
-
# class Iphone
-
# def self.matches(request)
-
# request.env["HTTP_USER_AGENT"] =~ /iPhone/
-
# end
-
# end
-
#
-
# An expected place for this code would be +lib/constraints+.
-
#
-
# This class is then used like this:
-
#
-
# constraints(Iphone) do
-
# resources :iphones
-
# end
-
1
def constraints(constraints = {})
-
scope(:constraints => constraints) { yield }
-
end
-
-
# Allows you to set default parameters for a route, such as this:
-
# defaults :id => 'home' do
-
# match 'scoped_pages/(:id)', :to => 'pages#show'
-
# end
-
# Using this, the +:id+ parameter here will default to 'home'.
-
1
def defaults(defaults = {})
-
scope(:defaults => defaults) { yield }
-
end
-
-
1
private
-
1
def scope_options #:nodoc:
-
28
@scope_options ||= private_methods.grep(/^merge_(.+)_scope$/) { $1.to_sym }
-
end
-
-
1
def merge_path_scope(parent, child) #:nodoc:
-
6
Mapper.normalize_path("#{parent}/#{child}")
-
end
-
-
1
def merge_shallow_path_scope(parent, child) #:nodoc:
-
Mapper.normalize_path("#{parent}/#{child}")
-
end
-
-
1
def merge_as_scope(parent, child) #:nodoc:
-
parent ? "#{parent}_#{child}" : child
-
end
-
-
1
def merge_shallow_prefix_scope(parent, child) #:nodoc:
-
parent ? "#{parent}_#{child}" : child
-
end
-
-
1
def merge_module_scope(parent, child) #:nodoc:
-
parent ? "#{parent}/#{child}" : child
-
end
-
-
1
def merge_controller_scope(parent, child) #:nodoc:
-
2
child
-
end
-
-
1
def merge_path_names_scope(parent, child) #:nodoc:
-
merge_options_scope(parent, child)
-
end
-
-
1
def merge_constraints_scope(parent, child) #:nodoc:
-
8
merge_options_scope(parent, child)
-
end
-
-
1
def merge_defaults_scope(parent, child) #:nodoc:
-
merge_options_scope(parent, child)
-
end
-
-
1
def merge_blocks_scope(parent, child) #:nodoc:
-
8
merged = parent ? parent.dup : []
-
8
merged << child if child
-
8
merged
-
end
-
-
1
def merge_options_scope(parent, child) #:nodoc:
-
16
(parent || {}).except(*override_keys(child)).merge(child)
-
end
-
-
1
def merge_shallow_scope(parent, child) #:nodoc:
-
child ? true : false
-
end
-
-
1
def override_keys(child) #:nodoc:
-
16
child.key?(:only) || child.key?(:except) ? [:only, :except] : []
-
end
-
end
-
-
# Resource routing allows you to quickly declare all of the common routes
-
# for a given resourceful controller. Instead of declaring separate routes
-
# for your +index+, +show+, +new+, +edit+, +create+, +update+ and +destroy+
-
# actions, a resourceful route declares them in a single line of code:
-
#
-
# resources :photos
-
#
-
# Sometimes, you have a resource that clients always look up without
-
# referencing an ID. A common example, /profile always shows the profile of
-
# the currently logged in user. In this case, you can use a singular resource
-
# to map /profile (rather than /profile/:id) to the show action.
-
#
-
# resource :profile
-
#
-
# It's common to have resources that are logically children of other
-
# resources:
-
#
-
# resources :magazines do
-
# resources :ads
-
# end
-
#
-
# You may wish to organize groups of controllers under a namespace. Most
-
# commonly, you might group a number of administrative controllers under
-
# an +admin+ namespace. You would place these controllers under the
-
# <tt>app/controllers/admin</tt> directory, and you can group them together
-
# in your router:
-
#
-
# namespace "admin" do
-
# resources :posts, :comments
-
# end
-
#
-
# By default the +:id+ parameter doesn't accept dots. If you need to
-
# use dots as part of the +:id+ parameter add a constraint which
-
# overrides this restriction, e.g:
-
#
-
# resources :articles, :id => /[^\/]+/
-
#
-
# This allows any character other than a slash as part of your +:id+.
-
#
-
1
module Resources
-
# CANONICAL_ACTIONS holds all actions that does not need a prefix or
-
# a path appended since they fit properly in their scope level.
-
1
VALID_ON_OPTIONS = [:new, :collection, :member]
-
1
RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except]
-
1
CANONICAL_ACTIONS = %w(index create new show update destroy)
-
-
1
class Resource #:nodoc:
-
1
DEFAULT_ACTIONS = [:index, :create, :new, :show, :update, :destroy, :edit]
-
-
1
attr_reader :controller, :path, :options
-
-
1
def initialize(entities, options = {})
-
2
@name = entities.to_s
-
2
@path = (options[:path] || @name).to_s
-
2
@controller = (options[:controller] || @name).to_s
-
2
@as = options[:as]
-
2
@options = options
-
end
-
-
1
def default_actions
-
14
self.class::DEFAULT_ACTIONS
-
end
-
-
1
def actions
-
14
if only = @options[:only]
-
Array(only).map(&:to_sym)
-
14
elsif except = @options[:except]
-
default_actions - Array(except).map(&:to_sym)
-
else
-
14
default_actions
-
end
-
end
-
-
1
def name
-
4
@as || @name
-
end
-
-
1
def plural
-
28
@plural ||= name.to_s
-
end
-
-
1
def singular
-
28
@singular ||= name.to_s.singularize
-
end
-
-
1
alias :member_name :singular
-
-
# Checks for uncountable plurals, and appends "_index" if the plural
-
# and singular form are the same.
-
1
def collection_name
-
14
singular == plural ? "#{plural}_index" : plural
-
end
-
-
1
def resource_scope
-
2
{ :controller => controller }
-
end
-
-
1
alias :collection_scope :path
-
-
1
def member_scope
-
2
"#{path}/:id"
-
end
-
-
1
def new_scope(new_path)
-
2
"#{path}/#{new_path}"
-
end
-
-
1
def nested_scope
-
"#{path}/:#{singular}_id"
-
end
-
-
end
-
-
1
class SingletonResource < Resource #:nodoc:
-
1
DEFAULT_ACTIONS = [:show, :create, :update, :destroy, :new, :edit]
-
-
1
def initialize(entities, options)
-
super
-
-
@as = nil
-
@controller = (options[:controller] || plural).to_s
-
@as = options[:as]
-
end
-
-
1
def plural
-
@plural ||= name.to_s.pluralize
-
end
-
-
1
def singular
-
@singular ||= name.to_s
-
end
-
-
1
alias :member_name :singular
-
1
alias :collection_name :singular
-
-
1
alias :member_scope :path
-
1
alias :nested_scope :path
-
end
-
-
1
def resources_path_names(options)
-
@scope[:path_names].merge!(options)
-
end
-
-
# Sometimes, you have a resource that clients always look up without
-
# referencing an ID. A common example, /profile always shows the
-
# profile of the currently logged in user. In this case, you can use
-
# a singular resource to map /profile (rather than /profile/:id) to
-
# the show action:
-
#
-
# resource :geocoder
-
#
-
# creates six different routes in your application, all mapping to
-
# the +GeoCoders+ controller (note that the controller is named after
-
# the plural):
-
#
-
# GET /geocoder/new
-
# POST /geocoder
-
# GET /geocoder
-
# GET /geocoder/edit
-
# PUT /geocoder
-
# DELETE /geocoder
-
#
-
# === Options
-
# Takes same options as +resources+.
-
1
def resource(*resources, &block)
-
options = resources.extract_options!
-
-
if apply_common_behavior_for(:resource, resources, options, &block)
-
return self
-
end
-
-
resource_scope(SingletonResource.new(resources.pop, options)) do
-
yield if block_given?
-
-
collection do
-
post :create
-
end if parent_resource.actions.include?(:create)
-
-
new do
-
get :new
-
end if parent_resource.actions.include?(:new)
-
-
member do
-
get :edit if parent_resource.actions.include?(:edit)
-
get :show if parent_resource.actions.include?(:show)
-
put :update if parent_resource.actions.include?(:update)
-
delete :destroy if parent_resource.actions.include?(:destroy)
-
end
-
end
-
-
self
-
end
-
-
# In Rails, a resourceful route provides a mapping between HTTP verbs
-
# and URLs and controller actions. By convention, each action also maps
-
# to particular CRUD operations in a database. A single entry in the
-
# routing file, such as
-
#
-
# resources :photos
-
#
-
# creates seven different routes in your application, all mapping to
-
# the +Photos+ controller:
-
#
-
# GET /photos/new
-
# POST /photos
-
# GET /photos/:id
-
# GET /photos/:id/edit
-
# PUT /photos/:id
-
# DELETE /photos/:id
-
#
-
# Resources can also be nested infinitely by using this block syntax:
-
#
-
# resources :photos do
-
# resources :comments
-
# end
-
#
-
# This generates the following comments routes:
-
#
-
# GET /photos/:id/comments/new
-
# POST /photos/:id/comments
-
# GET /photos/:id/comments/:id
-
# GET /photos/:id/comments/:id/edit
-
# PUT /photos/:id/comments/:id
-
# DELETE /photos/:id/comments/:id
-
#
-
# === Options
-
# Takes same options as <tt>Base#match</tt> as well as:
-
#
-
# [:path_names]
-
# Allows you to change the paths of the seven default actions.
-
# Paths not specified are not changed.
-
#
-
# resources :posts, :path_names => { :new => "brand_new" }
-
#
-
# The above example will now change /posts/new to /posts/brand_new
-
#
-
# [:only]
-
# Only generate routes for the given actions.
-
#
-
# resources :cows, :only => :show
-
# resources :cows, :only => [:show, :index]
-
#
-
# [:except]
-
# Generate all routes except for the given actions.
-
#
-
# resources :cows, :except => :show
-
# resources :cows, :except => [:show, :index]
-
#
-
# [:shallow]
-
# Generates shallow routes for nested resource(s). When placed on a parent resource,
-
# generates shallow routes for all nested resources.
-
#
-
# resources :posts, :shallow => true do
-
# resources :comments
-
# end
-
#
-
# Is the same as:
-
#
-
# resources :posts do
-
# resources :comments, :except => [:show, :edit, :update, :destroy]
-
# end
-
# resources :comments, :only => [:show, :edit, :update, :destroy]
-
#
-
# This allows URLs for resources that otherwise would be deeply nested such
-
# as a comment on a blog post like <tt>/posts/a-long-permalink/comments/1234</tt>
-
# to be shortened to just <tt>/comments/1234</tt>.
-
#
-
# [:shallow_path]
-
# Prefixes nested shallow routes with the specified path.
-
#
-
# scope :shallow_path => "sekret" do
-
# resources :posts do
-
# resources :comments, :shallow => true
-
# end
-
# end
-
#
-
# The +comments+ resource here will have the following routes generated for it:
-
#
-
# post_comments GET /posts/:post_id/comments(.:format)
-
# post_comments POST /posts/:post_id/comments(.:format)
-
# new_post_comment GET /posts/:post_id/comments/new(.:format)
-
# edit_comment GET /sekret/comments/:id/edit(.:format)
-
# comment GET /sekret/comments/:id(.:format)
-
# comment PUT /sekret/comments/:id(.:format)
-
# comment DELETE /sekret/comments/:id(.:format)
-
#
-
# === Examples
-
#
-
# # routes call <tt>Admin::PostsController</tt>
-
# resources :posts, :module => "admin"
-
#
-
# # resource actions are at /admin/posts.
-
# resources :posts, :path => "admin/posts"
-
1
def resources(*resources, &block)
-
2
options = resources.extract_options!
-
-
2
if apply_common_behavior_for(:resources, resources, options, &block)
-
return self
-
end
-
-
2
resource_scope(Resource.new(resources.pop, options)) do
-
2
yield if block_given?
-
-
2
collection do
-
2
get :index if parent_resource.actions.include?(:index)
-
2
post :create if parent_resource.actions.include?(:create)
-
end
-
-
new do
-
2
get :new
-
2
end if parent_resource.actions.include?(:new)
-
-
2
member do
-
2
get :edit if parent_resource.actions.include?(:edit)
-
2
get :show if parent_resource.actions.include?(:show)
-
2
put :update if parent_resource.actions.include?(:update)
-
2
delete :destroy if parent_resource.actions.include?(:destroy)
-
end
-
end
-
-
2
self
-
end
-
-
# To add a route to the collection:
-
#
-
# resources :photos do
-
# collection do
-
# get 'search'
-
# end
-
# end
-
#
-
# This will enable Rails to recognize paths such as <tt>/photos/search</tt>
-
# with GET, and route to the search action of +PhotosController+. It will also
-
# create the <tt>search_photos_url</tt> and <tt>search_photos_path</tt>
-
# route helpers.
-
1
def collection
-
2
unless resource_scope?
-
raise ArgumentError, "can't use collection outside resource(s) scope"
-
end
-
-
2
with_scope_level(:collection) do
-
2
scope(parent_resource.collection_scope) do
-
2
yield
-
end
-
end
-
end
-
-
# To add a member route, add a member block into the resource block:
-
#
-
# resources :photos do
-
# member do
-
# get 'preview'
-
# end
-
# end
-
#
-
# This will recognize <tt>/photos/1/preview</tt> with GET, and route to the
-
# preview action of +PhotosController+. It will also create the
-
# <tt>preview_photo_url</tt> and <tt>preview_photo_path</tt> helpers.
-
1
def member
-
2
unless resource_scope?
-
raise ArgumentError, "can't use member outside resource(s) scope"
-
end
-
-
2
with_scope_level(:member) do
-
2
scope(parent_resource.member_scope) do
-
2
yield
-
end
-
end
-
end
-
-
1
def new
-
2
unless resource_scope?
-
raise ArgumentError, "can't use new outside resource(s) scope"
-
end
-
-
2
with_scope_level(:new) do
-
2
scope(parent_resource.new_scope(action_path(:new))) do
-
2
yield
-
end
-
end
-
end
-
-
1
def nested
-
unless resource_scope?
-
raise ArgumentError, "can't use nested outside resource(s) scope"
-
end
-
-
with_scope_level(:nested) do
-
if shallow?
-
with_exclusive_scope do
-
if @scope[:shallow_path].blank?
-
scope(parent_resource.nested_scope, nested_options) { yield }
-
else
-
scope(@scope[:shallow_path], :as => @scope[:shallow_prefix]) do
-
scope(parent_resource.nested_scope, nested_options) { yield }
-
end
-
end
-
end
-
else
-
scope(parent_resource.nested_scope, nested_options) { yield }
-
end
-
end
-
end
-
-
# See ActionDispatch::Routing::Mapper::Scoping#namespace
-
1
def namespace(path, options = {})
-
if resource_scope?
-
nested { super }
-
else
-
super
-
end
-
end
-
-
1
def shallow
-
scope(:shallow => true, :shallow_path => @scope[:path]) do
-
yield
-
end
-
end
-
-
1
def shallow?
-
31
parent_resource.instance_of?(Resource) && @scope[:shallow]
-
end
-
-
1
def match(*args)
-
23
options = args.extract_options!.dup
-
23
options[:anchor] = true unless options.key?(:anchor)
-
-
23
if args.length > 1
-
args.each { |path| match(path, options.dup) }
-
return self
-
end
-
-
23
on = options.delete(:on)
-
23
if VALID_ON_OPTIONS.include?(on)
-
args.push(options)
-
return send(on){ match(*args) }
-
elsif on
-
raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
-
end
-
-
23
if @scope[:scope_level] == :resources
-
args.push(options)
-
return nested { match(*args) }
-
elsif @scope[:scope_level] == :resource
-
args.push(options)
-
return member { match(*args) }
-
end
-
-
23
action = args.first
-
23
path = path_for_action(action, options.delete(:path))
-
-
23
if action.to_s =~ /^[\w\/]+$/
-
17
options[:action] ||= action unless action.to_s.include?("/")
-
else
-
6
action = nil
-
end
-
-
23
if options.key?(:as) && !options[:as]
-
1
options.delete(:as)
-
else
-
22
options[:as] = name_for_action(options[:as], action)
-
end
-
-
23
super(path, options)
-
end
-
-
1
def root(options={})
-
1
if @scope[:scope_level] == :resources
-
with_scope_level(:root) do
-
scope(parent_resource.path) do
-
super(options)
-
end
-
end
-
else
-
1
super(options)
-
end
-
end
-
-
1
protected
-
-
1
def parent_resource #:nodoc:
-
109
@scope[:scope_level_resource]
-
end
-
-
1
def apply_common_behavior_for(method, resources, options, &block) #:nodoc:
-
2
if resources.length > 1
-
resources.each { |r| send(method, r, options, &block) }
-
return true
-
end
-
-
2
if resource_scope?
-
nested { send(method, resources.pop, options, &block) }
-
return true
-
end
-
-
2
options.keys.each do |k|
-
(options[:constraints] ||= {})[k] = options.delete(k) if options[k].is_a?(Regexp)
-
end
-
-
2
scope_options = options.slice!(*RESOURCE_OPTIONS)
-
2
unless scope_options.empty?
-
scope(scope_options) do
-
send(method, resources.pop, options, &block)
-
end
-
return true
-
end
-
-
2
unless action_options?(options)
-
2
options.merge!(scope_action_options) if scope_action_options?
-
end
-
-
2
false
-
end
-
-
1
def action_options?(options) #:nodoc:
-
2
options[:only] || options[:except]
-
end
-
-
1
def scope_action_options? #:nodoc:
-
2
@scope[:options].is_a?(Hash) && (@scope[:options][:only] || @scope[:options][:except])
-
end
-
-
1
def scope_action_options #:nodoc:
-
@scope[:options].slice(:only, :except)
-
end
-
-
1
def resource_scope? #:nodoc:
-
8
@scope[:scope_level].in?([:resource, :resources])
-
end
-
-
1
def resource_method_scope? #:nodoc:
-
37
@scope[:scope_level].in?([:collection, :member, :new])
-
end
-
-
1
def with_exclusive_scope
-
begin
-
old_name_prefix, old_path = @scope[:as], @scope[:path]
-
@scope[:as], @scope[:path] = nil, nil
-
-
with_scope_level(:exclusive) do
-
yield
-
end
-
ensure
-
@scope[:as], @scope[:path] = old_name_prefix, old_path
-
end
-
end
-
-
1
def with_scope_level(kind, resource = parent_resource)
-
8
old, @scope[:scope_level] = @scope[:scope_level], kind
-
8
old_resource, @scope[:scope_level_resource] = @scope[:scope_level_resource], resource
-
8
yield
-
ensure
-
8
@scope[:scope_level] = old
-
8
@scope[:scope_level_resource] = old_resource
-
end
-
-
1
def resource_scope(resource) #:nodoc:
-
2
with_scope_level(resource.is_a?(SingletonResource) ? :resource : :resources, resource) do
-
2
scope(parent_resource.resource_scope) do
-
2
yield
-
end
-
end
-
end
-
-
1
def nested_options #:nodoc:
-
{}.tap do |options|
-
options[:as] = parent_resource.member_name
-
options[:constraints] = { "#{parent_resource.singular}_id".to_sym => id_constraint } if id_constraint?
-
end
-
end
-
-
1
def id_constraint? #:nodoc:
-
@scope[:constraints] && @scope[:constraints][:id].is_a?(Regexp)
-
end
-
-
1
def id_constraint #:nodoc:
-
@scope[:constraints][:id]
-
end
-
-
1
def canonical_action?(action, flag) #:nodoc:
-
37
flag && resource_method_scope? && CANONICAL_ACTIONS.include?(action.to_s)
-
end
-
-
1
def shallow_scoping? #:nodoc:
-
31
shallow? && @scope[:scope_level] == :member
-
end
-
-
1
def path_for_action(action, path) #:nodoc:
-
23
prefix = shallow_scoping? ?
-
"#{@scope[:shallow_path]}/#{parent_resource.path}/:id" : @scope[:path]
-
-
23
path = if canonical_action?(action, path.blank?)
-
12
prefix.to_s
-
else
-
11
"#{prefix}/#{action_path(action, path)}"
-
end
-
end
-
-
1
def action_path(name, path = nil) #:nodoc:
-
13
path || @scope[:path_names][name.to_sym] || name.to_s
-
end
-
-
1
def prefix_name_for_action(as, action) #:nodoc:
-
22
if as
-
8
as.to_s
-
14
elsif !canonical_action?(action, @scope[:scope_level])
-
2
action.to_s
-
end
-
end
-
-
1
def name_for_action(as, action) #:nodoc:
-
22
prefix = prefix_name_for_action(as, action)
-
22
prefix = Mapper.normalize_name(prefix) if prefix
-
22
name_prefix = @scope[:as]
-
-
22
if parent_resource
-
14
return nil if as.nil? && action.nil?
-
-
14
collection_name = parent_resource.collection_name
-
14
member_name = parent_resource.member_name
-
end
-
-
22
name = case @scope[:scope_level]
-
when :nested
-
[name_prefix, prefix]
-
when :collection
-
4
[prefix, name_prefix, collection_name]
-
when :new
-
2
[prefix, :new, name_prefix, member_name]
-
when :member
-
8
[prefix, shallow_scoping? ? @scope[:shallow_prefix] : name_prefix, member_name]
-
when :root
-
[name_prefix, collection_name, prefix]
-
else
-
8
[name_prefix, member_name, prefix]
-
end
-
-
22
candidate = name.select(&:present?).join("_").presence
-
223
candidate unless as.nil? && @set.routes.find { |r| r.name == candidate }
-
end
-
end
-
-
1
module Shorthand #:nodoc:
-
1
def match(*args)
-
23
if args.size == 1 && args.last.is_a?(Hash)
-
7
options = args.pop
-
14
path, to = options.find { |name, value| name.is_a?(String) }
-
7
options.merge!(:to => to).delete(path)
-
7
super(path, options)
-
else
-
16
super
-
end
-
end
-
end
-
-
1
def initialize(set) #:nodoc:
-
2
@set = set
-
2
@scope = { :path_names => @set.resources_path_names }
-
end
-
-
1
include Base
-
1
include HttpHelpers
-
1
include Redirection
-
1
include Scoping
-
1
include Resources
-
1
include Shorthand
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
module Routing
-
# Polymorphic URL helpers are methods for smart resolution to a named route call when
-
# given an Active Record model instance. They are to be used in combination with
-
# ActionController::Resources.
-
#
-
# These methods are useful when you want to generate correct URL or path to a RESTful
-
# resource without having to know the exact type of the record in question.
-
#
-
# Nested resources and/or namespaces are also supported, as illustrated in the example:
-
#
-
# polymorphic_url([:admin, @article, @comment])
-
#
-
# results in:
-
#
-
# admin_article_comment_url(@article, @comment)
-
#
-
# == Usage within the framework
-
#
-
# Polymorphic URL helpers are used in a number of places throughout the \Rails framework:
-
#
-
# * <tt>url_for</tt>, so you can use it with a record as the argument, e.g.
-
# <tt>url_for(@article)</tt>;
-
# * ActionView::Helpers::FormHelper uses <tt>polymorphic_path</tt>, so you can write
-
# <tt>form_for(@article)</tt> without having to specify <tt>:url</tt> parameter for the form
-
# action;
-
# * <tt>redirect_to</tt> (which, in fact, uses <tt>url_for</tt>) so you can write
-
# <tt>redirect_to(post)</tt> in your controllers;
-
# * ActionView::Helpers::AtomFeedHelper, so you don't have to explicitly specify URLs
-
# for feed entries.
-
#
-
# == Prefixed polymorphic helpers
-
#
-
# In addition to <tt>polymorphic_url</tt> and <tt>polymorphic_path</tt> methods, a
-
# number of prefixed helpers are available as a shorthand to <tt>:action => "..."</tt>
-
# in options. Those are:
-
#
-
# * <tt>edit_polymorphic_url</tt>, <tt>edit_polymorphic_path</tt>
-
# * <tt>new_polymorphic_url</tt>, <tt>new_polymorphic_path</tt>
-
#
-
# Example usage:
-
#
-
# edit_polymorphic_path(@post) # => "/posts/1/edit"
-
# polymorphic_path(@post, :format => :pdf) # => "/posts/1.pdf"
-
#
-
# == Using with mounted engines
-
#
-
# If you use mounted engine, there is a possibility that you will need to use
-
# polymorphic_url pointing at engine's routes. To do that, just pass proxy used
-
# to reach engine's routes as a first argument:
-
#
-
# For example:
-
#
-
# polymorphic_url([blog, @post]) # it will call blog.post_path(@post)
-
# form_for([blog, @post]) # => "/blog/posts/1
-
#
-
1
module PolymorphicRoutes
-
# Constructs a call to a named RESTful route for the given record and returns the
-
# resulting URL string. For example:
-
#
-
# # calls post_url(post)
-
# polymorphic_url(post) # => "http://example.com/posts/1"
-
# polymorphic_url([blog, post]) # => "http://example.com/blogs/1/posts/1"
-
# polymorphic_url([:admin, blog, post]) # => "http://example.com/admin/blogs/1/posts/1"
-
# polymorphic_url([user, :blog, post]) # => "http://example.com/users/1/blog/posts/1"
-
# polymorphic_url(Comment) # => "http://example.com/comments"
-
#
-
# ==== Options
-
#
-
# * <tt>:action</tt> - Specifies the action prefix for the named route:
-
# <tt>:new</tt> or <tt>:edit</tt>. Default is no prefix.
-
# * <tt>:routing_type</tt> - Allowed values are <tt>:path</tt> or <tt>:url</tt>.
-
# Default is <tt>:url</tt>.
-
#
-
# ==== Examples
-
#
-
# # an Article record
-
# polymorphic_url(record) # same as article_url(record)
-
#
-
# # a Comment record
-
# polymorphic_url(record) # same as comment_url(record)
-
#
-
# # it recognizes new records and maps to the collection
-
# record = Comment.new
-
# polymorphic_url(record) # same as comments_url()
-
#
-
# # the class of a record will also map to the collection
-
# polymorphic_url(Comment) # same as comments_url()
-
#
-
1
def polymorphic_url(record_or_hash_or_array, options = {})
-
if record_or_hash_or_array.kind_of?(Array)
-
record_or_hash_or_array = record_or_hash_or_array.compact
-
if record_or_hash_or_array.first.is_a?(ActionDispatch::Routing::RoutesProxy)
-
proxy = record_or_hash_or_array.shift
-
end
-
record_or_hash_or_array = record_or_hash_or_array[0] if record_or_hash_or_array.size == 1
-
end
-
-
record = extract_record(record_or_hash_or_array)
-
record = record.to_model if record.respond_to?(:to_model)
-
-
args = Array === record_or_hash_or_array ?
-
record_or_hash_or_array.dup :
-
[ record_or_hash_or_array ]
-
-
inflection = if options[:action] && options[:action].to_s == "new"
-
args.pop
-
:singular
-
elsif (record.respond_to?(:persisted?) && !record.persisted?)
-
args.pop
-
:plural
-
elsif record.is_a?(Class)
-
args.pop
-
:plural
-
else
-
:singular
-
end
-
-
args.delete_if {|arg| arg.is_a?(Symbol) || arg.is_a?(String)}
-
named_route = build_named_route_call(record_or_hash_or_array, inflection, options)
-
-
url_options = options.except(:action, :routing_type)
-
unless url_options.empty?
-
args.last.kind_of?(Hash) ? args.last.merge!(url_options) : args << url_options
-
end
-
-
(proxy || self).send(named_route, *args)
-
end
-
-
# Returns the path component of a URL for the given record. It uses
-
# <tt>polymorphic_url</tt> with <tt>:routing_type => :path</tt>.
-
1
def polymorphic_path(record_or_hash_or_array, options = {})
-
polymorphic_url(record_or_hash_or_array, options.merge(:routing_type => :path))
-
end
-
-
1
%w(edit new).each do |action|
-
2
module_eval <<-EOT, __FILE__, __LINE__ + 1
-
def #{action}_polymorphic_url(record_or_hash, options = {}) # def edit_polymorphic_url(record_or_hash, options = {})
-
polymorphic_url( # polymorphic_url(
-
record_or_hash, # record_or_hash,
-
options.merge(:action => "#{action}")) # options.merge(:action => "edit"))
-
end # end
-
#
-
def #{action}_polymorphic_path(record_or_hash, options = {}) # def edit_polymorphic_path(record_or_hash, options = {})
-
polymorphic_url( # polymorphic_url(
-
record_or_hash, # record_or_hash,
-
options.merge(:action => "#{action}", :routing_type => :path)) # options.merge(:action => "edit", :routing_type => :path))
-
end # end
-
EOT
-
end
-
-
1
private
-
1
def action_prefix(options)
-
options[:action] ? "#{options[:action]}_" : ''
-
end
-
-
1
def routing_type(options)
-
options[:routing_type] || :url
-
end
-
-
1
def build_named_route_call(records, inflection, options = {})
-
if records.is_a?(Array)
-
record = records.pop
-
route = records.map do |parent|
-
if parent.is_a?(Symbol) || parent.is_a?(String)
-
parent
-
else
-
ActiveModel::Naming.route_key(parent).singularize
-
end
-
end
-
else
-
record = extract_record(records)
-
route = []
-
end
-
-
if record.is_a?(Symbol) || record.is_a?(String)
-
route << record
-
elsif record
-
route << ActiveModel::Naming.route_key(record)
-
route = [route.join("_").singularize] if inflection == :singular
-
route << "index" if ActiveModel::Naming.uncountable?(record) && inflection == :plural
-
else
-
raise ArgumentError, "Nil location provided. Can't build URI."
-
end
-
-
route << routing_type(options)
-
-
action_prefix(options) + route.join("_")
-
end
-
-
1
def extract_record(record_or_hash_or_array)
-
case record_or_hash_or_array
-
when Array; record_or_hash_or_array.last
-
when Hash; record_or_hash_or_array[:id]
-
else record_or_hash_or_array
-
end
-
end
-
end
-
end
-
end
-
-
1
require 'action_dispatch/http/request'
-
-
1
module ActionDispatch
-
1
module Routing
-
1
module Redirection
-
-
# Redirect any path to another path:
-
#
-
# match "/stories" => redirect("/posts")
-
#
-
# You can also use interpolation in the supplied redirect argument:
-
#
-
# match 'docs/:article', :to => redirect('/wiki/%{article}')
-
#
-
# Alternatively you can use one of the other syntaxes:
-
#
-
# The block version of redirect allows for the easy encapsulation of any logic associated with
-
# the redirect in question. Either the params and request are supplied as arguments, or just
-
# params, depending of how many arguments your block accepts. A string is required as a
-
# return value.
-
#
-
# match 'jokes/:number', :to => redirect do |params, request|
-
# path = (params[:number].to_i.even? ? "/wheres-the-beef" : "/i-love-lamp")
-
# "http://#{request.host_with_port}/#{path}"
-
# end
-
#
-
# The options version of redirect allows you to supply only the parts of the url which need
-
# to change, it also supports interpolation of the path similar to the first example.
-
#
-
# match 'stores/:name', :to => redirect(:subdomain => 'stores', :path => '/%{name}')
-
# match 'stores/:name(*all)', :to => redirect(:subdomain => 'stores', :path => '/%{name}%{all}')
-
#
-
# Finally, an object which responds to call can be supplied to redirect, allowing you to reuse
-
# common redirect routes. The call method must accept two arguments, params and request, and return
-
# a string.
-
#
-
# match 'accounts/:name' => redirect(SubdomainRedirector.new('api'))
-
#
-
1
def redirect(*args, &block)
-
options = args.last.is_a?(Hash) ? args.pop : {}
-
status = options.delete(:status) || 301
-
-
path = args.shift
-
-
path_proc = if path.is_a?(String)
-
proc { |params| (params.empty? || !path.match(/%\{\w*\}/)) ? path : (path % params) }
-
elsif options.any?
-
options_proc(options)
-
elsif path.respond_to?(:call)
-
proc { |params, request| path.call(params, request) }
-
elsif block
-
block
-
else
-
raise ArgumentError, "redirection argument not supported"
-
end
-
-
redirection_proc(status, path_proc)
-
end
-
-
1
private
-
-
1
def options_proc(options)
-
proc do |params, request|
-
path = if options[:path].nil?
-
request.path
-
elsif params.empty? || !options[:path].match(/%\{\w*\}/)
-
options.delete(:path)
-
else
-
(options.delete(:path) % params)
-
end
-
-
default_options = {
-
:protocol => request.protocol,
-
:host => request.host,
-
:port => request.optional_port,
-
:path => path,
-
:params => request.query_parameters
-
}
-
-
ActionDispatch::Http::URL.url_for(options.reverse_merge(default_options))
-
end
-
end
-
-
1
def redirection_proc(status, path_proc)
-
lambda do |env|
-
req = Request.new(env)
-
-
params = [req.symbolized_path_parameters]
-
params << req if path_proc.arity > 1
-
-
uri = URI.parse(path_proc.call(*params))
-
uri.scheme ||= req.scheme
-
uri.host ||= req.host
-
uri.port ||= req.port unless req.standard_port?
-
-
body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
-
-
headers = {
-
'Location' => uri.to_s,
-
'Content-Type' => 'text/html',
-
'Content-Length' => body.length.to_s
-
}
-
-
[ status, headers, [body] ]
-
end
-
end
-
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/deprecation'
-
-
1
module ActionDispatch
-
1
module Routing
-
1
class Route #:nodoc:
-
1
attr_reader :app, :conditions, :defaults, :name
-
1
attr_reader :path, :requirements, :set
-
-
1
def initialize(set, app, conditions, requirements, defaults, name, anchor)
-
23
@set = set
-
23
@app = app
-
23
@defaults = defaults
-
23
@name = name
-
-
# FIXME: we should not be doing this much work in a constructor.
-
-
23
@requirements = requirements.merge(defaults)
-
23
@requirements.delete(:controller) if @requirements[:controller].is_a?(Regexp)
-
23
@requirements.delete_if { |k, v|
-
44
v == Regexp.compile("[^#{SEPARATORS.join}]+")
-
}
-
-
23
if path = conditions[:path_info]
-
23
@path = path
-
23
conditions[:path_info] = ::Rack::Mount::Strexp.compile(path, requirements, SEPARATORS, anchor)
-
end
-
-
23
@verbs = conditions[:request_method] || []
-
-
23
@conditions = conditions.dup
-
-
# Rack-Mount requires that :request_method be a regular expression.
-
# :request_method represents the HTTP verb that matches this route.
-
#
-
# Here we munge values before they get sent on to rack-mount.
-
23
@conditions[:request_method] = %r[^#{verb}$] unless @verbs.empty?
-
23
@conditions[:path_info] = Rack::Mount::RegexpWithNamedGroups.new(@conditions[:path_info]) if @conditions[:path_info]
-
67
@conditions.delete_if{ |k,v| k != :path_info && !valid_condition?(k) }
-
67
@requirements.delete_if{ |k,v| !valid_condition?(k) }
-
end
-
-
1
def verb
-
21
@verbs.join '|'
-
end
-
-
1
def segment_keys
-
132
@segment_keys ||= conditions[:path_info].names.compact.map { |key| key.to_sym }
-
end
-
-
1
def to_a
-
[@app, @conditions, @defaults, @name]
-
end
-
1
deprecate :to_a
-
-
1
def to_s
-
@to_s ||= begin
-
"%-6s %-40s %s" % [(verb || :any).to_s.upcase, path, requirements.inspect]
-
end
-
end
-
-
1
private
-
1
def valid_condition?(method)
-
65
segment_keys.include?(method) || set.valid_conditions.include?(method)
-
end
-
end
-
end
-
end
-
1
require 'rack/mount'
-
1
require 'forwardable'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/object/to_query'
-
1
require 'active_support/core_ext/hash/slice'
-
1
require 'active_support/core_ext/module/remove_method'
-
-
1
module ActionDispatch
-
1
module Routing
-
1
class RouteSet #:nodoc:
-
1
PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
-
-
1
class Dispatcher #:nodoc:
-
1
def initialize(options={})
-
22
@defaults = options[:defaults]
-
22
@glob_param = options.delete(:glob)
-
22
@controllers = {}
-
end
-
-
1
def call(env)
-
params = env[PARAMETERS_KEY]
-
prepare_params!(params)
-
-
# Just raise undefined constant errors if a controller was specified as default.
-
unless controller = controller(params, @defaults.key?(:controller))
-
return [404, {'X-Cascade' => 'pass'}, []]
-
end
-
-
dispatch(controller, params[:action], env)
-
end
-
-
1
def prepare_params!(params)
-
merge_default_action!(params)
-
split_glob_param!(params) if @glob_param
-
end
-
-
# If this is a default_controller (i.e. a controller specified by the user)
-
# we should raise an error in case it's not found, because it usually means
-
# an user error. However, if the controller was retrieved through a dynamic
-
# segment, as in :controller(/:action), we should simply return nil and
-
# delegate the control back to Rack cascade. Besides, if this is not a default
-
# controller, it means we should respect the @scope[:module] parameter.
-
1
def controller(params, default_controller=true)
-
if params && params.key?(:controller)
-
controller_param = params[:controller]
-
controller_reference(controller_param)
-
end
-
rescue NameError => e
-
raise ActionController::RoutingError, e.message, e.backtrace if default_controller
-
end
-
-
1
private
-
-
1
def controller_reference(controller_param)
-
controller_name = "#{controller_param.camelize}Controller"
-
-
unless controller = @controllers[controller_param]
-
controller = @controllers[controller_param] =
-
ActiveSupport::Dependencies.reference(controller_name)
-
end
-
controller.get(controller_name)
-
end
-
-
1
def dispatch(controller, action, env)
-
controller.action(action).call(env)
-
end
-
-
1
def merge_default_action!(params)
-
params[:action] ||= 'index'
-
end
-
-
1
def split_glob_param!(params)
-
params[@glob_param] = params[@glob_param].split('/').map { |v| URI.parser.unescape(v) }
-
end
-
end
-
-
# A NamedRouteCollection instance is a collection of named routes, and also
-
# maintains an anonymous module that can be used to install helpers for the
-
# named routes.
-
1
class NamedRouteCollection #:nodoc:
-
1
include Enumerable
-
1
attr_reader :routes, :helpers, :module
-
-
1
def initialize
-
1
clear!
-
end
-
-
1
def helper_names
-
self.module.instance_methods.map(&:to_s)
-
end
-
-
1
def clear!
-
3
@routes = {}
-
3
@helpers = []
-
-
@module ||= Module.new do
-
1
instance_methods.each { |selector| remove_method(selector) }
-
3
end
-
end
-
-
1
def add(name, route)
-
16
routes[name.to_sym] = route
-
16
define_named_route_methods(name, route)
-
end
-
-
1
def get(name)
-
routes[name.to_sym]
-
end
-
-
1
alias []= add
-
1
alias [] get
-
1
alias clear clear!
-
-
1
def each
-
routes.each { |name, route| yield name, route }
-
self
-
end
-
-
1
def names
-
routes.keys
-
end
-
-
1
def length
-
routes.length
-
end
-
-
1
def reset!
-
old_routes = routes.dup
-
clear!
-
old_routes.each do |name, route|
-
add(name, route)
-
end
-
end
-
-
1
def install(destinations = [ActionController::Base, ActionView::Base], regenerate = false)
-
1
reset! if regenerate
-
1
Array(destinations).each do |dest|
-
1
dest.__send__(:include, @module)
-
end
-
end
-
-
1
private
-
1
def url_helper_name(name, kind = :url)
-
32
:"#{name}_#{kind}"
-
end
-
-
1
def hash_access_name(name, kind = :url)
-
64
:"hash_for_#{name}_#{kind}"
-
end
-
-
1
def define_named_route_methods(name, route)
-
16
{:url => {:only_path => false}, :path => {:only_path => true}}.each do |kind, opts|
-
32
hash = route.defaults.merge(:use_route => name).merge(opts)
-
32
define_hash_access route, name, kind, hash
-
32
define_url_helper route, name, kind, hash
-
end
-
end
-
-
1
def define_hash_access(route, name, kind, options)
-
32
selector = hash_access_name(name, kind)
-
-
# We use module_eval to avoid leaks
-
32
@module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
-
remove_possible_method :#{selector}
-
def #{selector}(*args)
-
options = args.extract_options!
-
-
if args.any?
-
options[:_positional_args] = args
-
options[:_positional_keys] = #{route.segment_keys.inspect}
-
end
-
-
options ? #{options.inspect}.merge(options) : #{options.inspect}
-
end
-
protected :#{selector}
-
END_EVAL
-
32
helpers << selector
-
end
-
-
# Create a url helper allowing ordered parameters to be associated
-
# with corresponding dynamic segments, so you can do:
-
#
-
# foo_url(bar, baz, bang)
-
#
-
# Instead of:
-
#
-
# foo_url(:bar => bar, :baz => baz, :bang => bang)
-
#
-
# Also allow options hash, so you can do:
-
#
-
# foo_url(bar, baz, bang, :sort_by => 'baz')
-
#
-
1
def define_url_helper(route, name, kind, options)
-
32
selector = url_helper_name(name, kind)
-
32
hash_access_method = hash_access_name(name, kind)
-
-
32
@module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
-
remove_possible_method :#{selector}
-
def #{selector}(*args)
-
url_for(#{hash_access_method}(*args))
-
end
-
END_EVAL
-
32
helpers << selector
-
end
-
end
-
-
1
attr_accessor :set, :routes, :named_routes, :default_scope
-
1
attr_accessor :disable_clear_and_finalize, :resources_path_names
-
1
attr_accessor :default_url_options, :request_class, :valid_conditions
-
-
1
def self.default_resources_path_names
-
1
{ :new => 'new', :edit => 'edit' }
-
end
-
-
1
def initialize(request_class = ActionDispatch::Request)
-
1
self.routes = []
-
1
self.named_routes = NamedRouteCollection.new
-
1
self.resources_path_names = self.class.default_resources_path_names.dup
-
1
self.default_url_options = {}
-
-
1
self.request_class = request_class
-
217
self.valid_conditions = request_class.public_instance_methods.map { |m| m.to_sym }
-
1
self.valid_conditions.delete(:id)
-
1
self.valid_conditions.push(:controller, :action)
-
-
1
@append = []
-
1
@prepend = []
-
1
@disable_clear_and_finalize = false
-
1
clear!
-
end
-
-
1
def draw(&block)
-
1
clear! unless @disable_clear_and_finalize
-
1
eval_block(block)
-
1
finalize! unless @disable_clear_and_finalize
-
nil
-
end
-
-
1
def append(&block)
-
@append << block
-
end
-
-
1
def prepend(&block)
-
1
@prepend << block
-
end
-
-
1
def eval_block(block)
-
2
if block.arity == 1
-
raise "You are using the old router DSL which has been removed in Rails 3.1. " <<
-
"Please check how to update your routes file at: http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/ " <<
-
"or add the rails_legacy_mapper gem to your Gemfile"
-
end
-
2
mapper = Mapper.new(self)
-
2
if default_scope
-
mapper.with_default_scope(default_scope, &block)
-
else
-
2
mapper.instance_exec(&block)
-
end
-
end
-
-
1
def finalize!
-
1
return if @finalized
-
1
@append.each { |blk| eval_block(blk) }
-
1
@finalized = true
-
1
@set.freeze
-
end
-
-
1
def clear!
-
2
@finalized = false
-
2
routes.clear
-
2
named_routes.clear
-
2
@set = ::Rack::Mount::RouteSet.new(
-
:parameters_key => PARAMETERS_KEY,
-
:request_class => request_class
-
)
-
3
@prepend.each { |blk| eval_block(blk) }
-
end
-
-
1
def install_helpers(destinations = [ActionController::Base, ActionView::Base], regenerate_code = false)
-
3
Array(destinations).each { |d| d.module_eval { include Helpers } }
-
1
named_routes.install(destinations, regenerate_code)
-
end
-
-
1
module MountedHelpers
-
end
-
-
1
def mounted_helpers
-
1
MountedHelpers
-
end
-
-
1
def define_mounted_helper(name)
-
1
return if MountedHelpers.method_defined?(name)
-
-
1
routes = self
-
1
MountedHelpers.class_eval do
-
1
define_method "_#{name}" do
-
RoutesProxy.new(routes, self._routes_context)
-
end
-
end
-
-
1
MountedHelpers.class_eval <<-RUBY
-
def #{name}
-
@#{name} ||= _#{name}
-
end
-
RUBY
-
end
-
-
1
def url_helpers
-
@url_helpers ||= begin
-
1
routes = self
-
-
1
helpers = Module.new do
-
1
extend ActiveSupport::Concern
-
1
include UrlFor
-
-
1
@_routes = routes
-
1
class << self
-
1
delegate :url_for, :to => '@_routes'
-
end
-
1
extend routes.named_routes.module
-
-
# ROUTES TODO: install_helpers isn't great... can we make a module with the stuff that
-
# we can include?
-
# Yes plz - JP
-
1
included do
-
1
routes.install_helpers(self)
-
1
singleton_class.send(:redefine_method, :_routes) { routes }
-
end
-
-
1
define_method(:_routes) { @_routes || routes }
-
end
-
-
1
helpers
-
4
end
-
end
-
-
1
def empty?
-
routes.empty?
-
end
-
-
1
def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil, anchor = true)
-
23
raise ArgumentError, "Invalid route name: '#{name}'" unless name.blank? || name.to_s.match(/^[_a-z]\w*$/i)
-
23
route = Route.new(self, app, conditions, requirements, defaults, name, anchor)
-
23
@set.add_route(route.app, route.conditions, route.defaults, route.name)
-
23
named_routes[name] = route if name
-
23
routes << route
-
23
route
-
end
-
-
1
class Generator #:nodoc:
-
1
PARAMETERIZE = {
-
:parameterize => lambda do |name, value|
-
if name == :controller
-
value
-
elsif value.is_a?(Array)
-
value.map { |v| Rack::Mount::Utils.escape_uri(v.to_param) }.join('/')
-
else
-
return nil unless param = value.to_param
-
param.split('/').map { |v| Rack::Mount::Utils.escape_uri(v) }.join("/")
-
end
-
end
-
}
-
-
1
attr_reader :options, :recall, :set, :named_route
-
-
1
def initialize(options, recall, set, extras = false)
-
@named_route = options.delete(:use_route)
-
@options = options.dup
-
@recall = recall.dup
-
@set = set
-
@extras = extras
-
-
normalize_options!
-
normalize_controller_action_id!
-
use_relative_controller!
-
controller.sub!(%r{^/}, '') if controller
-
handle_nil_action!
-
end
-
-
1
def controller
-
@controller ||= @options[:controller]
-
end
-
-
1
def current_controller
-
@recall[:controller]
-
end
-
-
1
def use_recall_for(key)
-
if @recall[key] && (!@options.key?(key) || @options[key] == @recall[key])
-
if named_route_exists?
-
@options[key] = @recall.delete(key) if segment_keys.include?(key)
-
else
-
@options[key] = @recall.delete(key)
-
end
-
end
-
end
-
-
1
def normalize_options!
-
# If an explicit :controller was given, always make :action explicit
-
# too, so that action expiry works as expected for things like
-
#
-
# generate({:controller => 'content'}, {:controller => 'content', :action => 'show'})
-
#
-
# (the above is from the unit tests). In the above case, because the
-
# controller was explicitly given, but no action, the action is implied to
-
# be "index", not the recalled action of "show".
-
-
if options[:controller]
-
options[:action] ||= 'index'
-
options[:controller] = options[:controller].to_s
-
end
-
-
if options[:action]
-
options[:action] = options[:action].to_s
-
end
-
end
-
-
# This pulls :controller, :action, and :id out of the recall.
-
# The recall key is only used if there is no key in the options
-
# or if the key in the options is identical. If any of
-
# :controller, :action or :id is not found, don't pull any
-
# more keys from the recall.
-
1
def normalize_controller_action_id!
-
@recall[:action] ||= 'index' if current_controller
-
-
use_recall_for(:controller) or return
-
use_recall_for(:action) or return
-
use_recall_for(:id)
-
end
-
-
# if the current controller is "foo/bar/baz" and :controller => "baz/bat"
-
# is specified, the controller becomes "foo/baz/bat"
-
1
def use_relative_controller!
-
if !named_route && different_controller?
-
old_parts = current_controller.split('/')
-
size = controller.count("/") + 1
-
parts = old_parts[0...-size] << controller
-
@controller = @options[:controller] = parts.join("/")
-
end
-
end
-
-
# This handles the case of :action => nil being explicitly passed.
-
# It is identical to :action => "index"
-
1
def handle_nil_action!
-
if options.has_key?(:action) && options[:action].nil?
-
options[:action] = 'index'
-
end
-
recall[:action] = options.delete(:action) if options[:action] == 'index'
-
end
-
-
1
def generate
-
path, params = @set.set.generate(:path_info, named_route, options, recall, PARAMETERIZE)
-
-
raise_routing_error unless path
-
-
return [path, params.keys] if @extras
-
-
[path, params]
-
rescue Rack::Mount::RoutingError
-
raise_routing_error
-
end
-
-
1
def raise_routing_error
-
raise ActionController::RoutingError, "No route matches #{options.inspect}"
-
end
-
-
1
def different_controller?
-
return false unless current_controller
-
controller.to_param != current_controller.to_param
-
end
-
-
1
private
-
1
def named_route_exists?
-
named_route && set.named_routes[named_route]
-
end
-
-
1
def segment_keys
-
set.named_routes[named_route].segment_keys
-
end
-
end
-
-
# Generate the path indicated by the arguments, and return an array of
-
# the keys that were not used to generate it.
-
1
def extra_keys(options, recall={})
-
generate_extras(options, recall).last
-
end
-
-
1
def generate_extras(options, recall={})
-
generate(options, recall, true)
-
end
-
-
1
def generate(options, recall = {}, extras = false)
-
Generator.new(options, recall, self, extras).generate
-
end
-
-
1
RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length,
-
:trailing_slash, :anchor, :params, :only_path, :script_name]
-
-
1
def _generate_prefix(options = {})
-
nil
-
end
-
-
1
def url_for(options)
-
finalize!
-
options = (options || {}).reverse_merge!(default_url_options)
-
-
handle_positional_args(options)
-
-
user, password = extract_authentication(options)
-
path_segments = options.delete(:_path_segments)
-
script_name = options.delete(:script_name)
-
-
path = (script_name.blank? ? _generate_prefix(options) : script_name.chomp('/')).to_s
-
-
path_options = options.except(*RESERVED_OPTIONS)
-
path_options = yield(path_options) if block_given?
-
-
path_addition, params = generate(path_options, path_segments || {})
-
path << path_addition
-
-
ActionDispatch::Http::URL.url_for(options.merge({
-
:path => path,
-
:params => params,
-
:user => user,
-
:password => password
-
}))
-
end
-
-
1
def call(env)
-
finalize!
-
@set.call(env)
-
end
-
-
1
def recognize_path(path, environment = {})
-
method = (environment[:method] || "GET").to_s.upcase
-
path = Rack::Mount::Utils.normalize_path(path) unless path =~ %r{://}
-
-
begin
-
env = Rack::MockRequest.env_for(path, {:method => method})
-
rescue URI::InvalidURIError => e
-
raise ActionController::RoutingError, e.message
-
end
-
-
req = @request_class.new(env)
-
@set.recognize(req) do |route, matches, params|
-
params.each do |key, value|
-
if value.is_a?(String)
-
value = value.dup.force_encoding(Encoding::BINARY) if value.encoding_aware?
-
params[key] = URI.parser.unescape(value)
-
end
-
end
-
-
dispatcher = route.app
-
while dispatcher.is_a?(Mapper::Constraints) && dispatcher.matches?(env) do
-
dispatcher = dispatcher.app
-
end
-
-
if dispatcher.is_a?(Dispatcher) && dispatcher.controller(params, false)
-
dispatcher.prepare_params!(params)
-
return params
-
end
-
end
-
-
raise ActionController::RoutingError, "No route matches #{path.inspect}"
-
end
-
-
1
private
-
-
1
def extract_authentication(options)
-
if options[:user] && options[:password]
-
[options.delete(:user), options.delete(:password)]
-
else
-
nil
-
end
-
end
-
-
1
def handle_positional_args(options)
-
return unless args = options.delete(:_positional_args)
-
-
keys = options.delete(:_positional_keys)
-
keys -= options.keys if args.size < keys.size - 1 # take format into account
-
-
# Tell url_for to skip default_url_options
-
options.merge!(Hash[args.zip(keys).map { |v, k| [k, v] }])
-
end
-
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
module Routing
-
# In <tt>config/routes.rb</tt> you define URL-to-controller mappings, but the reverse
-
# is also possible: an URL can be generated from one of your routing definitions.
-
# URL generation functionality is centralized in this module.
-
#
-
# See ActionDispatch::Routing for general information about routing and routes.rb.
-
#
-
# <b>Tip:</b> If you need to generate URLs from your models or some other place,
-
# then ActionController::UrlFor is what you're looking for. Read on for
-
# an introduction.
-
#
-
# == URL generation from parameters
-
#
-
# As you may know, some functions, such as ActionController::Base#url_for
-
# and ActionView::Helpers::UrlHelper#link_to, can generate URLs given a set
-
# of parameters. For example, you've probably had the chance to write code
-
# like this in one of your views:
-
#
-
# <%= link_to('Click here', :controller => 'users',
-
# :action => 'new', :message => 'Welcome!') %>
-
# # => "/users/new?message=Welcome%21"
-
#
-
# link_to, and all other functions that require URL generation functionality,
-
# actually use ActionController::UrlFor under the hood. And in particular,
-
# they use the ActionController::UrlFor#url_for method. One can generate
-
# the same path as the above example by using the following code:
-
#
-
# include UrlFor
-
# url_for(:controller => 'users',
-
# :action => 'new',
-
# :message => 'Welcome!',
-
# :only_path => true)
-
# # => "/users/new?message=Welcome%21"
-
#
-
# Notice the <tt>:only_path => true</tt> part. This is because UrlFor has no
-
# information about the website hostname that your Rails app is serving. So if you
-
# want to include the hostname as well, then you must also pass the <tt>:host</tt>
-
# argument:
-
#
-
# include UrlFor
-
# url_for(:controller => 'users',
-
# :action => 'new',
-
# :message => 'Welcome!',
-
# :host => 'www.example.com') # Changed this.
-
# # => "http://www.example.com/users/new?message=Welcome%21"
-
#
-
# By default, all controllers and views have access to a special version of url_for,
-
# that already knows what the current hostname is. So if you use url_for in your
-
# controllers or your views, then you don't need to explicitly pass the <tt>:host</tt>
-
# argument.
-
#
-
# For convenience reasons, mailers provide a shortcut for ActionController::UrlFor#url_for.
-
# So within mailers, you only have to type 'url_for' instead of 'ActionController::UrlFor#url_for'
-
# in full. However, mailers don't have hostname information, and what's why you'll still
-
# have to specify the <tt>:host</tt> argument when generating URLs in mailers.
-
#
-
#
-
# == URL generation for named routes
-
#
-
# UrlFor also allows one to access methods that have been auto-generated from
-
# named routes. For example, suppose that you have a 'users' resource in your
-
# <tt>config/routes.rb</tt>:
-
#
-
# resources :users
-
#
-
# This generates, among other things, the method <tt>users_path</tt>. By default,
-
# this method is accessible from your controllers, views and mailers. If you need
-
# to access this auto-generated method from other places (such as a model), then
-
# you can do that by including ActionController::UrlFor in your class:
-
#
-
# class User < ActiveRecord::Base
-
# include Rails.application.routes.url_helpers
-
#
-
# def base_uri
-
# user_path(self)
-
# end
-
# end
-
#
-
# User.find(1).base_uri # => "/users/1"
-
#
-
1
module UrlFor
-
1
extend ActiveSupport::Concern
-
1
include PolymorphicRoutes
-
-
1
included do
-
# TODO: with_routing extends @controller with url_helpers, trickling down to including this module which overrides its default_url_options
-
5
unless method_defined?(:default_url_options)
-
# Including in a class uses an inheritable hash. Modules get a plain hash.
-
5
if respond_to?(:class_attribute)
-
5
class_attribute :default_url_options
-
else
-
mattr_accessor :default_url_options
-
remove_method :default_url_options
-
end
-
-
5
self.default_url_options = {}
-
end
-
end
-
-
1
def initialize(*)
-
@_routes = nil
-
super
-
end
-
-
1
def url_options
-
default_url_options
-
end
-
-
# Generate a url based on the options provided, default_url_options and the
-
# routes defined in routes.rb. The following options are supported:
-
#
-
# * <tt>:only_path</tt> - If true, the relative url is returned. Defaults to +false+.
-
# * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
-
# * <tt>:host</tt> - Specifies the host the link should be targeted at.
-
# If <tt>:only_path</tt> is false, this option must be
-
# provided either explicitly, or via +default_url_options+.
-
# * <tt>:subdomain</tt> - Specifies the subdomain of the link, using the +tld_length+
-
# to split the domain from the host.
-
# * <tt>:domain</tt> - Specifies the domain of the link, using the +tld_length+
-
# to split the subdomain from the host.
-
# * <tt>:tld_length</tt> - Number of labels the TLD id composed of, only used if
-
# <tt>:subdomain</tt> or <tt>:domain</tt> are supplied. Defaults to
-
# <tt>ActionDispatch::Http::URL.tld_length</tt>, which in turn defaults to 1.
-
# * <tt>:port</tt> - Optionally specify the port to connect to.
-
# * <tt>:anchor</tt> - An anchor name to be appended to the path.
-
# * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
-
#
-
# Any other key (<tt>:controller</tt>, <tt>:action</tt>, etc.) given to
-
# +url_for+ is forwarded to the Routes module.
-
#
-
# Examples:
-
#
-
# url_for :controller => 'tasks', :action => 'testing', :host => 'somehost.org', :port => '8080' # => 'http://somehost.org:8080/tasks/testing'
-
# url_for :controller => 'tasks', :action => 'testing', :host => 'somehost.org', :anchor => 'ok', :only_path => true # => '/tasks/testing#ok'
-
# url_for :controller => 'tasks', :action => 'testing', :trailing_slash => true # => 'http://somehost.org/tasks/testing/'
-
# url_for :controller => 'tasks', :action => 'testing', :host => 'somehost.org', :number => '33' # => 'http://somehost.org/tasks/testing?number=33'
-
1
def url_for(options = nil)
-
case options
-
when String
-
options
-
when nil, Hash
-
_routes.url_for((options || {}).reverse_merge(url_options).symbolize_keys)
-
else
-
polymorphic_url(options)
-
end
-
end
-
-
1
protected
-
1
def _with_routes(routes)
-
old_routes, @_routes = @_routes, routes
-
yield
-
ensure
-
@_routes = old_routes
-
end
-
-
1
def _routes_context
-
self
-
end
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
module Assertions
-
1
autoload :DomAssertions, 'action_dispatch/testing/assertions/dom'
-
1
autoload :ResponseAssertions, 'action_dispatch/testing/assertions/response'
-
1
autoload :RoutingAssertions, 'action_dispatch/testing/assertions/routing'
-
1
autoload :SelectorAssertions, 'action_dispatch/testing/assertions/selector'
-
1
autoload :TagAssertions, 'action_dispatch/testing/assertions/tag'
-
-
1
extend ActiveSupport::Concern
-
-
1
include DomAssertions
-
1
include ResponseAssertions
-
1
include RoutingAssertions
-
1
include SelectorAssertions
-
1
include TagAssertions
-
end
-
end
-
-
1
require 'action_controller/vendor/html-scanner'
-
-
1
module ActionDispatch
-
1
module Assertions
-
1
module DomAssertions
-
# \Test two HTML strings for equivalency (e.g., identical up to reordering of attributes)
-
#
-
# ==== Examples
-
#
-
# # assert that the referenced method generates the appropriate HTML string
-
# assert_dom_equal '<a href="http://www.example.com">Apples</a>', link_to("Apples", "http://www.example.com")
-
#
-
1
def assert_dom_equal(expected, actual, message = "")
-
expected_dom = HTML::Document.new(expected).root
-
actual_dom = HTML::Document.new(actual).root
-
full_message = build_message(message, "<?> expected to be == to\n<?>.", expected_dom.to_s, actual_dom.to_s)
-
-
assert_block(full_message) { expected_dom == actual_dom }
-
end
-
-
# The negated form of +assert_dom_equivalent+.
-
#
-
# ==== Examples
-
#
-
# # assert that the referenced method does not generate the specified HTML string
-
# assert_dom_not_equal '<a href="http://www.example.com">Apples</a>', link_to("Oranges", "http://www.example.com")
-
#
-
1
def assert_dom_not_equal(expected, actual, message = "")
-
expected_dom = HTML::Document.new(expected).root
-
actual_dom = HTML::Document.new(actual).root
-
full_message = build_message(message, "<?> expected to be != to\n<?>.", expected_dom.to_s, actual_dom.to_s)
-
-
assert_block(full_message) { expected_dom != actual_dom }
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/inclusion'
-
-
1
module ActionDispatch
-
1
module Assertions
-
# A small suite of assertions that test responses from \Rails applications.
-
1
module ResponseAssertions
-
1
extend ActiveSupport::Concern
-
-
# Asserts that the response is one of the following types:
-
#
-
# * <tt>:success</tt> - Status code was 200
-
# * <tt>:redirect</tt> - Status code was in the 300-399 range
-
# * <tt>:missing</tt> - Status code was 404
-
# * <tt>:error</tt> - Status code was in the 500-599 range
-
#
-
# You can also pass an explicit status number like <tt>assert_response(501)</tt>
-
# or its symbolic equivalent <tt>assert_response(:not_implemented)</tt>.
-
# See Rack::Utils::SYMBOL_TO_STATUS_CODE for a full list.
-
#
-
# ==== Examples
-
#
-
# # assert that the response was a redirection
-
# assert_response :redirect
-
#
-
# # assert that the response code was status code 401 (unauthorized)
-
# assert_response 401
-
#
-
1
def assert_response(type, message = nil)
-
validate_request!
-
-
if type.in?([:success, :missing, :redirect, :error]) && @response.send("#{type}?")
-
assert_block("") { true } # to count the assertion
-
elsif type.is_a?(Fixnum) && @response.response_code == type
-
assert_block("") { true } # to count the assertion
-
elsif type.is_a?(Symbol) && @response.response_code == Rack::Utils::SYMBOL_TO_STATUS_CODE[type]
-
assert_block("") { true } # to count the assertion
-
else
-
flunk(build_message(message, "Expected response to be a <?>, but was <?>", type, @response.response_code))
-
end
-
end
-
-
# Assert that the redirection options passed in match those of the redirect called in the latest action.
-
# This match can be partial, such that <tt>assert_redirected_to(:controller => "weblog")</tt> will also
-
# match the redirection of <tt>redirect_to(:controller => "weblog", :action => "show")</tt> and so on.
-
#
-
# ==== Examples
-
#
-
# # assert that the redirection was to the "index" action on the WeblogController
-
# assert_redirected_to :controller => "weblog", :action => "index"
-
#
-
# # assert that the redirection was to the named route login_url
-
# assert_redirected_to login_url
-
#
-
# # assert that the redirection was to the url for @customer
-
# assert_redirected_to @customer
-
#
-
1
def assert_redirected_to(options = {}, message=nil)
-
validate_request!
-
-
assert_response(:redirect, message)
-
return true if options == @response.location
-
-
redirected_to_after_normalisation = normalize_argument_to_redirection(@response.location)
-
options_after_normalisation = normalize_argument_to_redirection(options)
-
-
if redirected_to_after_normalisation != options_after_normalisation
-
flunk "Expected response to be a redirect to <#{options_after_normalisation}> but was a redirect to <#{redirected_to_after_normalisation}>"
-
end
-
end
-
-
1
private
-
# Proxy to to_param if the object will respond to it.
-
1
def parameterize(value)
-
value.respond_to?(:to_param) ? value.to_param : value
-
end
-
-
1
def normalize_argument_to_redirection(fragment)
-
case fragment
-
when %r{^\w[A-Za-z\d+.-]*:.*}
-
fragment
-
when String
-
@request.protocol + @request.host_with_port + fragment
-
when :back
-
raise RedirectBackError unless refer = @request.headers["Referer"]
-
refer
-
else
-
@controller.url_for(fragment)
-
end.gsub(/[\r\n]/, '')
-
end
-
-
1
def validate_request!
-
unless @request.is_a?(ActionDispatch::Request)
-
raise ArgumentError, "@request must be an ActionDispatch::Request"
-
end
-
end
-
end
-
end
-
end
-
1
require 'uri'
-
1
require 'active_support/core_ext/hash/diff'
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
-
1
module ActionDispatch
-
1
module Assertions
-
# Suite of assertions to test routes generated by \Rails and the handling of requests made to them.
-
1
module RoutingAssertions
-
# Asserts that the routing of the given +path+ was handled correctly and that the parsed options (given in the +expected_options+ hash)
-
# match +path+. Basically, it asserts that \Rails recognizes the route given by +expected_options+.
-
#
-
# Pass a hash in the second argument (+path+) to specify the request method. This is useful for routes
-
# requiring a specific HTTP method. The hash should contain a :path with the incoming request path
-
# and a :method containing the required HTTP verb.
-
#
-
# # assert that POSTing to /items will call the create action on ItemsController
-
# assert_recognizes({:controller => 'items', :action => 'create'}, {:path => 'items', :method => :post})
-
#
-
# You can also pass in +extras+ with a hash containing URL parameters that would normally be in the query string. This can be used
-
# to assert that values in the query string string will end up in the params hash correctly. To test query strings you must use the
-
# extras argument, appending the query string on the path directly will not work. For example:
-
#
-
# # assert that a path of '/items/list/1?view=print' returns the correct options
-
# assert_recognizes({:controller => 'items', :action => 'list', :id => '1', :view => 'print'}, 'items/list/1', { :view => "print" })
-
#
-
# The +message+ parameter allows you to pass in an error message that is displayed upon failure.
-
#
-
# ==== Examples
-
# # Check the default route (i.e., the index action)
-
# assert_recognizes({:controller => 'items', :action => 'index'}, 'items')
-
#
-
# # Test a specific action
-
# assert_recognizes({:controller => 'items', :action => 'list'}, 'items/list')
-
#
-
# # Test an action with a parameter
-
# assert_recognizes({:controller => 'items', :action => 'destroy', :id => '1'}, 'items/destroy/1')
-
#
-
# # Test a custom route
-
# assert_recognizes({:controller => 'items', :action => 'show', :id => '1'}, 'view/item1')
-
1
def assert_recognizes(expected_options, path, extras={}, message=nil)
-
request = recognized_request_for(path)
-
-
expected_options = expected_options.clone
-
extras.each_key { |key| expected_options.delete key } unless extras.nil?
-
-
expected_options.stringify_keys!
-
msg = build_message(message, "The recognized options <?> did not match <?>, difference: <?>",
-
request.path_parameters, expected_options, expected_options.diff(request.path_parameters))
-
assert_equal(expected_options, request.path_parameters, msg)
-
end
-
-
# Asserts that the provided options can be used to generate the provided path. This is the inverse of +assert_recognizes+.
-
# The +extras+ parameter is used to tell the request the names and values of additional request parameters that would be in
-
# a query string. The +message+ parameter allows you to specify a custom error message for assertion failures.
-
#
-
# The +defaults+ parameter is unused.
-
#
-
# ==== Examples
-
# # Asserts that the default action is generated for a route with no action
-
# assert_generates "/items", :controller => "items", :action => "index"
-
#
-
# # Tests that the list action is properly routed
-
# assert_generates "/items/list", :controller => "items", :action => "list"
-
#
-
# # Tests the generation of a route with a parameter
-
# assert_generates "/items/list/1", { :controller => "items", :action => "list", :id => "1" }
-
#
-
# # Asserts that the generated route gives us our custom route
-
# assert_generates "changesets/12", { :controller => 'scm', :action => 'show_diff', :revision => "12" }
-
1
def assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)
-
if expected_path =~ %r{://}
-
begin
-
uri = URI.parse(expected_path)
-
expected_path = uri.path.to_s.empty? ? "/" : uri.path
-
rescue URI::InvalidURIError => e
-
raise ActionController::RoutingError, e.message
-
end
-
else
-
expected_path = "/#{expected_path}" unless expected_path.first == '/'
-
end
-
# Load routes.rb if it hasn't been loaded.
-
-
generated_path, extra_keys = @routes.generate_extras(options, defaults)
-
found_extras = options.reject {|k, v| ! extra_keys.include? k}
-
-
msg = build_message(message, "found extras <?>, not <?>", found_extras, extras)
-
assert_equal(extras, found_extras, msg)
-
-
msg = build_message(message, "The generated path <?> did not match <?>", generated_path,
-
expected_path)
-
assert_equal(expected_path, generated_path, msg)
-
end
-
-
# Asserts that path and options match both ways; in other words, it verifies that <tt>path</tt> generates
-
# <tt>options</tt> and then that <tt>options</tt> generates <tt>path</tt>. This essentially combines +assert_recognizes+
-
# and +assert_generates+ into one step.
-
#
-
# The +extras+ hash allows you to specify options that would normally be provided as a query string to the action. The
-
# +message+ parameter allows you to specify a custom error message to display upon failure.
-
#
-
# ==== Examples
-
# # Assert a basic route: a controller with the default action (index)
-
# assert_routing '/home', :controller => 'home', :action => 'index'
-
#
-
# # Test a route generated with a specific controller, action, and parameter (id)
-
# assert_routing '/entries/show/23', :controller => 'entries', :action => 'show', :id => 23
-
#
-
# # Assert a basic route (controller + default action), with an error message if it fails
-
# assert_routing '/store', { :controller => 'store', :action => 'index' }, {}, {}, 'Route for store index not generated properly'
-
#
-
# # Tests a route, providing a defaults hash
-
# assert_routing 'controller/action/9', {:id => "9", :item => "square"}, {:controller => "controller", :action => "action"}, {}, {:item => "square"}
-
#
-
# # Tests a route with a HTTP method
-
# assert_routing({ :method => 'put', :path => '/product/321' }, { :controller => "product", :action => "update", :id => "321" })
-
1
def assert_routing(path, options, defaults={}, extras={}, message=nil)
-
assert_recognizes(options, path, extras, message)
-
-
controller, default_controller = options[:controller], defaults[:controller]
-
if controller && controller.include?(?/) && default_controller && default_controller.include?(?/)
-
options[:controller] = "/#{controller}"
-
end
-
-
generate_options = options.dup.delete_if{ |k,v| defaults.key?(k) }
-
assert_generates(path.is_a?(Hash) ? path[:path] : path, generate_options, defaults, extras, message)
-
end
-
-
# A helper to make it easier to test different route configurations.
-
# This method temporarily replaces @routes
-
# with a new RouteSet instance.
-
#
-
# The new instance is yielded to the passed block. Typically the block
-
# will create some routes using <tt>map.draw { map.connect ... }</tt>:
-
#
-
# with_routing do |set|
-
# set.draw do |map|
-
# map.connect ':controller/:action/:id'
-
# assert_equal(
-
# ['/content/10/show', {}],
-
# map.generate(:controller => 'content', :id => 10, :action => 'show')
-
# end
-
# end
-
# end
-
#
-
1
def with_routing
-
old_routes, @routes = @routes, ActionDispatch::Routing::RouteSet.new
-
if defined?(@controller) && @controller
-
old_controller, @controller = @controller, @controller.clone
-
_routes = @routes
-
-
# Unfortunately, there is currently an abstraction leak between AC::Base
-
# and AV::Base which requires having the URL helpers in both AC and AV.
-
# To do this safely at runtime for tests, we need to bump up the helper serial
-
# to that the old AV subclass isn't cached.
-
#
-
# TODO: Make this unnecessary
-
@controller.singleton_class.send(:include, _routes.url_helpers)
-
@controller.view_context_class = Class.new(@controller.view_context_class) do
-
include _routes.url_helpers
-
end
-
end
-
yield @routes
-
ensure
-
@routes = old_routes
-
if defined?(@controller) && @controller
-
@controller = old_controller
-
end
-
end
-
-
# ROUTES TODO: These assertions should really work in an integration context
-
1
def method_missing(selector, *args, &block)
-
if defined?(@controller) && @controller && @routes && @routes.named_routes.helpers.include?(selector)
-
@controller.send(selector, *args, &block)
-
else
-
super
-
end
-
end
-
-
1
private
-
# Recognizes the route for a given path.
-
1
def recognized_request_for(path)
-
if path.is_a?(Hash)
-
method = path[:method]
-
path = path[:path]
-
else
-
method = :get
-
end
-
-
# Assume given controller
-
request = ActionController::TestRequest.new
-
-
if path =~ %r{://}
-
begin
-
uri = URI.parse(path)
-
request.env["rack.url_scheme"] = uri.scheme || "http"
-
request.host = uri.host if uri.host
-
request.port = uri.port if uri.port
-
request.path = uri.path.to_s.empty? ? "/" : uri.path
-
rescue URI::InvalidURIError => e
-
raise ActionController::RoutingError, e.message
-
end
-
else
-
path = "/#{path}" unless path.first == "/"
-
request.path = path
-
end
-
-
request.request_method = method if method
-
-
params = @routes.recognize_path(path, { :method => method })
-
request.path_parameters = params.with_indifferent_access
-
-
request
-
end
-
end
-
end
-
end
-
1
require 'action_controller/vendor/html-scanner'
-
1
require 'active_support/core_ext/object/inclusion'
-
-
#--
-
# Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
-
# Under MIT and/or CC By license.
-
#++
-
-
1
module ActionDispatch
-
1
module Assertions
-
1
NO_STRIP = %w{pre script style textarea}
-
-
# Adds the +assert_select+ method for use in Rails functional
-
# test cases, which can be used to make assertions on the response HTML of a controller
-
# action. You can also call +assert_select+ within another +assert_select+ to
-
# make assertions on elements selected by the enclosing assertion.
-
#
-
# Use +css_select+ to select elements without making an assertions, either
-
# from the response HTML or elements selected by the enclosing assertion.
-
#
-
# In addition to HTML responses, you can make the following assertions:
-
#
-
# * +assert_select_encoded+ - Assertions on HTML encoded inside XML, for example for dealing with feed item descriptions.
-
# * +assert_select_email+ - Assertions on the HTML body of an e-mail.
-
#
-
# Also see HTML::Selector to learn how to use selectors.
-
1
module SelectorAssertions
-
# Select and return all matching elements.
-
#
-
# If called with a single argument, uses that argument as a selector
-
# to match all elements of the current page. Returns an empty array
-
# if no match is found.
-
#
-
# If called with two arguments, uses the first argument as the base
-
# element and the second argument as the selector. Attempts to match the
-
# base element and any of its children. Returns an empty array if no
-
# match is found.
-
#
-
# The selector may be a CSS selector expression (String), an expression
-
# with substitution values (Array) or an HTML::Selector object.
-
#
-
# ==== Examples
-
# # Selects all div tags
-
# divs = css_select("div")
-
#
-
# # Selects all paragraph tags and does something interesting
-
# pars = css_select("p")
-
# pars.each do |par|
-
# # Do something fun with paragraphs here...
-
# end
-
#
-
# # Selects all list items in unordered lists
-
# items = css_select("ul>li")
-
#
-
# # Selects all form tags and then all inputs inside the form
-
# forms = css_select("form")
-
# forms.each do |form|
-
# inputs = css_select(form, "input")
-
# ...
-
# end
-
#
-
1
def css_select(*args)
-
# See assert_select to understand what's going on here.
-
arg = args.shift
-
-
if arg.is_a?(HTML::Node)
-
root = arg
-
arg = args.shift
-
elsif arg == nil
-
raise ArgumentError, "First argument is either selector or element to select, but nil found. Perhaps you called assert_select with an element that does not exist?"
-
elsif defined?(@selected) && @selected
-
matches = []
-
-
@selected.each do |selected|
-
subset = css_select(selected, HTML::Selector.new(arg.dup, args.dup))
-
subset.each do |match|
-
matches << match unless matches.any? { |m| m.equal?(match) }
-
end
-
end
-
-
return matches
-
else
-
root = response_from_page
-
end
-
-
case arg
-
when String
-
selector = HTML::Selector.new(arg, args)
-
when Array
-
selector = HTML::Selector.new(*arg)
-
when HTML::Selector
-
selector = arg
-
else raise ArgumentError, "Expecting a selector as the first argument"
-
end
-
-
selector.select(root)
-
end
-
-
# An assertion that selects elements and makes one or more equality tests.
-
#
-
# If the first argument is an element, selects all matching elements
-
# starting from (and including) that element and all its children in
-
# depth-first order.
-
#
-
# If no element if specified, calling +assert_select+ selects from the
-
# response HTML unless +assert_select+ is called from within an +assert_select+ block.
-
#
-
# When called with a block +assert_select+ passes an array of selected elements
-
# to the block. Calling +assert_select+ from the block, with no element specified,
-
# runs the assertion on the complete set of elements selected by the enclosing assertion.
-
# Alternatively the array may be iterated through so that +assert_select+ can be called
-
# separately for each element.
-
#
-
#
-
# ==== Example
-
# If the response contains two ordered lists, each with four list elements then:
-
# assert_select "ol" do |elements|
-
# elements.each do |element|
-
# assert_select element, "li", 4
-
# end
-
# end
-
#
-
# will pass, as will:
-
# assert_select "ol" do
-
# assert_select "li", 8
-
# end
-
#
-
# The selector may be a CSS selector expression (String), an expression
-
# with substitution values, or an HTML::Selector object.
-
#
-
# === Equality Tests
-
#
-
# The equality test may be one of the following:
-
# * <tt>true</tt> - Assertion is true if at least one element selected.
-
# * <tt>false</tt> - Assertion is true if no element selected.
-
# * <tt>String/Regexp</tt> - Assertion is true if the text value of at least
-
# one element matches the string or regular expression.
-
# * <tt>Integer</tt> - Assertion is true if exactly that number of
-
# elements are selected.
-
# * <tt>Range</tt> - Assertion is true if the number of selected
-
# elements fit the range.
-
# If no equality test specified, the assertion is true if at least one
-
# element selected.
-
#
-
# To perform more than one equality tests, use a hash with the following keys:
-
# * <tt>:text</tt> - Narrow the selection to elements that have this text
-
# value (string or regexp).
-
# * <tt>:html</tt> - Narrow the selection to elements that have this HTML
-
# content (string or regexp).
-
# * <tt>:count</tt> - Assertion is true if the number of selected elements
-
# is equal to this value.
-
# * <tt>:minimum</tt> - Assertion is true if the number of selected
-
# elements is at least this value.
-
# * <tt>:maximum</tt> - Assertion is true if the number of selected
-
# elements is at most this value.
-
#
-
# If the method is called with a block, once all equality tests are
-
# evaluated the block is called with an array of all matched elements.
-
#
-
# ==== Examples
-
#
-
# # At least one form element
-
# assert_select "form"
-
#
-
# # Form element includes four input fields
-
# assert_select "form input", 4
-
#
-
# # Page title is "Welcome"
-
# assert_select "title", "Welcome"
-
#
-
# # Page title is "Welcome" and there is only one title element
-
# assert_select "title", {:count => 1, :text => "Welcome"},
-
# "Wrong title or more than one title element"
-
#
-
# # Page contains no forms
-
# assert_select "form", false, "This page must contain no forms"
-
#
-
# # Test the content and style
-
# assert_select "body div.header ul.menu"
-
#
-
# # Use substitution values
-
# assert_select "ol>li#?", /item-\d+/
-
#
-
# # All input fields in the form have a name
-
# assert_select "form input" do
-
# assert_select "[name=?]", /.+/ # Not empty
-
# end
-
1
def assert_select(*args, &block)
-
# Start with optional element followed by mandatory selector.
-
arg = args.shift
-
@selected ||= nil
-
-
if arg.is_a?(HTML::Node)
-
# First argument is a node (tag or text, but also HTML root),
-
# so we know what we're selecting from.
-
root = arg
-
arg = args.shift
-
elsif arg == nil
-
# This usually happens when passing a node/element that
-
# happens to be nil.
-
raise ArgumentError, "First argument is either selector or element to select, but nil found. Perhaps you called assert_select with an element that does not exist?"
-
elsif @selected
-
root = HTML::Node.new(nil)
-
root.children.concat @selected
-
else
-
# Otherwise just operate on the response document.
-
root = response_from_page
-
end
-
-
# First or second argument is the selector: string and we pass
-
# all remaining arguments. Array and we pass the argument. Also
-
# accepts selector itself.
-
case arg
-
when String
-
selector = HTML::Selector.new(arg, args)
-
when Array
-
selector = HTML::Selector.new(*arg)
-
when HTML::Selector
-
selector = arg
-
else raise ArgumentError, "Expecting a selector as the first argument"
-
end
-
-
# Next argument is used for equality tests.
-
equals = {}
-
case arg = args.shift
-
when Hash
-
equals = arg
-
when String, Regexp
-
equals[:text] = arg
-
when Integer
-
equals[:count] = arg
-
when Range
-
equals[:minimum] = arg.begin
-
equals[:maximum] = arg.end
-
when FalseClass
-
equals[:count] = 0
-
when NilClass, TrueClass
-
equals[:minimum] = 1
-
else raise ArgumentError, "I don't understand what you're trying to match"
-
end
-
-
# By default we're looking for at least one match.
-
if equals[:count]
-
equals[:minimum] = equals[:maximum] = equals[:count]
-
else
-
equals[:minimum] = 1 unless equals[:minimum]
-
end
-
-
# Last argument is the message we use if the assertion fails.
-
message = args.shift
-
#- message = "No match made with selector #{selector.inspect}" unless message
-
if args.shift
-
raise ArgumentError, "Not expecting that last argument, you either have too many arguments, or they're the wrong type"
-
end
-
-
matches = selector.select(root)
-
# If text/html, narrow down to those elements that match it.
-
content_mismatch = nil
-
if match_with = equals[:text]
-
matches.delete_if do |match|
-
text = ""
-
stack = match.children.reverse
-
while node = stack.pop
-
if node.tag?
-
stack.concat node.children.reverse
-
else
-
content = node.content
-
text << content
-
end
-
end
-
text.strip! unless NO_STRIP.include?(match.name)
-
unless match_with.is_a?(Regexp) ? (text =~ match_with) : (text == match_with.to_s)
-
content_mismatch ||= build_message(message, "<?> expected but was\n<?>.", match_with, text)
-
true
-
end
-
end
-
elsif match_with = equals[:html]
-
matches.delete_if do |match|
-
html = match.children.map(&:to_s).join
-
html.strip! unless NO_STRIP.include?(match.name)
-
unless match_with.is_a?(Regexp) ? (html =~ match_with) : (html == match_with.to_s)
-
content_mismatch ||= build_message(message, "<?> expected but was\n<?>.", match_with, html)
-
true
-
end
-
end
-
end
-
# Expecting foo found bar element only if found zero, not if
-
# found one but expecting two.
-
message ||= content_mismatch if matches.empty?
-
# Test minimum/maximum occurrence.
-
min, max, count = equals[:minimum], equals[:maximum], equals[:count]
-
message = message || %(Expected #{count_description(min, max, count)} matching "#{selector.to_s}", found #{matches.size}.)
-
if count
-
assert matches.size == count, message
-
else
-
assert matches.size >= min, message if min
-
assert matches.size <= max, message if max
-
end
-
-
# If a block is given call that block. Set @selected to allow
-
# nested assert_select, which can be nested several levels deep.
-
if block_given? && !matches.empty?
-
begin
-
in_scope, @selected = @selected, matches
-
yield matches
-
ensure
-
@selected = in_scope
-
end
-
end
-
-
# Returns all matches elements.
-
matches
-
end
-
-
1
def count_description(min, max, count) #:nodoc:
-
pluralize = lambda {|word, quantity| word << (quantity == 1 ? '' : 's')}
-
-
if min && max && (max != min)
-
"between #{min} and #{max} elements"
-
elsif min && max && max == min && count
-
"exactly #{count} #{pluralize['element', min]}"
-
elsif min && !(min == 1 && max == 1)
-
"at least #{min} #{pluralize['element', min]}"
-
elsif max
-
"at most #{max} #{pluralize['element', max]}"
-
end
-
end
-
-
# Extracts the content of an element, treats it as encoded HTML and runs
-
# nested assertion on it.
-
#
-
# You typically call this method within another assertion to operate on
-
# all currently selected elements. You can also pass an element or array
-
# of elements.
-
#
-
# The content of each element is un-encoded, and wrapped in the root
-
# element +encoded+. It then calls the block with all un-encoded elements.
-
#
-
# ==== Examples
-
# # Selects all bold tags from within the title of an ATOM feed's entries (perhaps to nab a section name prefix)
-
# assert_select_feed :atom, 1.0 do
-
# # Select each entry item and then the title item
-
# assert_select "entry>title" do
-
# # Run assertions on the encoded title elements
-
# assert_select_encoded do
-
# assert_select "b"
-
# end
-
# end
-
# end
-
#
-
#
-
# # Selects all paragraph tags from within the description of an RSS feed
-
# assert_select_feed :rss, 2.0 do
-
# # Select description element of each feed item.
-
# assert_select "channel>item>description" do
-
# # Run assertions on the encoded elements.
-
# assert_select_encoded do
-
# assert_select "p"
-
# end
-
# end
-
# end
-
1
def assert_select_encoded(element = nil, &block)
-
case element
-
when Array
-
elements = element
-
when HTML::Node
-
elements = [element]
-
when nil
-
unless elements = @selected
-
raise ArgumentError, "First argument is optional, but must be called from a nested assert_select"
-
end
-
else
-
raise ArgumentError, "Argument is optional, and may be node or array of nodes"
-
end
-
-
fix_content = lambda do |node|
-
# Gets around a bug in the Rails 1.1 HTML parser.
-
node.content.gsub(/<!\[CDATA\[(.*)(\]\]>)?/m) { Rack::Utils.escapeHTML($1) }
-
end
-
-
selected = elements.map do |_element|
-
text = _element.children.select{ |c| not c.tag? }.map{ |c| fix_content[c] }.join
-
root = HTML::Document.new(CGI.unescapeHTML("<encoded>#{text}</encoded>")).root
-
css_select(root, "encoded:root", &block)[0]
-
end
-
-
begin
-
old_selected, @selected = @selected, selected
-
assert_select ":root", &block
-
ensure
-
@selected = old_selected
-
end
-
end
-
-
# Extracts the body of an email and runs nested assertions on it.
-
#
-
# You must enable deliveries for this assertion to work, use:
-
# ActionMailer::Base.perform_deliveries = true
-
#
-
# ==== Examples
-
#
-
# assert_select_email do
-
# assert_select "h1", "Email alert"
-
# end
-
#
-
# assert_select_email do
-
# items = assert_select "ol>li"
-
# items.each do
-
# # Work with items here...
-
# end
-
# end
-
#
-
1
def assert_select_email(&block)
-
deliveries = ActionMailer::Base.deliveries
-
assert !deliveries.empty?, "No e-mail in delivery list"
-
-
for delivery in deliveries
-
for part in delivery.parts
-
if part["Content-Type"].to_s =~ /^text\/html\W/
-
root = HTML::Document.new(part.body).root
-
assert_select root, ":root", &block
-
end
-
end
-
end
-
end
-
-
1
protected
-
# +assert_select+ and +css_select+ call this to obtain the content in the HTML page.
-
1
def response_from_page
-
html_document.root
-
end
-
end
-
end
-
end
-
1
require 'action_controller/vendor/html-scanner'
-
-
1
module ActionDispatch
-
1
module Assertions
-
# Pair of assertions to testing elements in the HTML output of the response.
-
1
module TagAssertions
-
# Asserts that there is a tag/node/element in the body of the response
-
# that meets all of the given conditions. The +conditions+ parameter must
-
# be a hash of any of the following keys (all are optional):
-
#
-
# * <tt>:tag</tt>: the node type must match the corresponding value
-
# * <tt>:attributes</tt>: a hash. The node's attributes must match the
-
# corresponding values in the hash.
-
# * <tt>:parent</tt>: a hash. The node's parent must match the
-
# corresponding hash.
-
# * <tt>:child</tt>: a hash. At least one of the node's immediate children
-
# must meet the criteria described by the hash.
-
# * <tt>:ancestor</tt>: a hash. At least one of the node's ancestors must
-
# meet the criteria described by the hash.
-
# * <tt>:descendant</tt>: a hash. At least one of the node's descendants
-
# must meet the criteria described by the hash.
-
# * <tt>:sibling</tt>: a hash. At least one of the node's siblings must
-
# meet the criteria described by the hash.
-
# * <tt>:after</tt>: a hash. The node must be after any sibling meeting
-
# the criteria described by the hash, and at least one sibling must match.
-
# * <tt>:before</tt>: a hash. The node must be before any sibling meeting
-
# the criteria described by the hash, and at least one sibling must match.
-
# * <tt>:children</tt>: a hash, for counting children of a node. Accepts
-
# the keys:
-
# * <tt>:count</tt>: either a number or a range which must equal (or
-
# include) the number of children that match.
-
# * <tt>:less_than</tt>: the number of matching children must be less
-
# than this number.
-
# * <tt>:greater_than</tt>: the number of matching children must be
-
# greater than this number.
-
# * <tt>:only</tt>: another hash consisting of the keys to use
-
# to match on the children, and only matching children will be
-
# counted.
-
# * <tt>:content</tt>: the textual content of the node must match the
-
# given value. This will not match HTML tags in the body of a
-
# tag--only text.
-
#
-
# Conditions are matched using the following algorithm:
-
#
-
# * if the condition is a string, it must be a substring of the value.
-
# * if the condition is a regexp, it must match the value.
-
# * if the condition is a number, the value must match number.to_s.
-
# * if the condition is +true+, the value must not be +nil+.
-
# * if the condition is +false+ or +nil+, the value must be +nil+.
-
#
-
# === Examples
-
#
-
# # Assert that there is a "span" tag
-
# assert_tag :tag => "span"
-
#
-
# # Assert that there is a "span" tag with id="x"
-
# assert_tag :tag => "span", :attributes => { :id => "x" }
-
#
-
# # Assert that there is a "span" tag using the short-hand
-
# assert_tag :span
-
#
-
# # Assert that there is a "span" tag with id="x" using the short-hand
-
# assert_tag :span, :attributes => { :id => "x" }
-
#
-
# # Assert that there is a "span" inside of a "div"
-
# assert_tag :tag => "span", :parent => { :tag => "div" }
-
#
-
# # Assert that there is a "span" somewhere inside a table
-
# assert_tag :tag => "span", :ancestor => { :tag => "table" }
-
#
-
# # Assert that there is a "span" with at least one "em" child
-
# assert_tag :tag => "span", :child => { :tag => "em" }
-
#
-
# # Assert that there is a "span" containing a (possibly nested)
-
# # "strong" tag.
-
# assert_tag :tag => "span", :descendant => { :tag => "strong" }
-
#
-
# # Assert that there is a "span" containing between 2 and 4 "em" tags
-
# # as immediate children
-
# assert_tag :tag => "span",
-
# :children => { :count => 2..4, :only => { :tag => "em" } }
-
#
-
# # Get funky: assert that there is a "div", with an "ul" ancestor
-
# # and an "li" parent (with "class" = "enum"), and containing a
-
# # "span" descendant that contains text matching /hello world/
-
# assert_tag :tag => "div",
-
# :ancestor => { :tag => "ul" },
-
# :parent => { :tag => "li",
-
# :attributes => { :class => "enum" } },
-
# :descendant => { :tag => "span",
-
# :child => /hello world/ }
-
#
-
# <b>Please note</b>: +assert_tag+ and +assert_no_tag+ only work
-
# with well-formed XHTML. They recognize a few tags as implicitly self-closing
-
# (like br and hr and such) but will not work correctly with tags
-
# that allow optional closing tags (p, li, td). <em>You must explicitly
-
# close all of your tags to use these assertions.</em>
-
1
def assert_tag(*opts)
-
opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
-
tag = find_tag(opts)
-
assert tag, "expected tag, but no tag found matching #{opts.inspect} in:\n#{@response.body.inspect}"
-
end
-
-
# Identical to +assert_tag+, but asserts that a matching tag does _not_
-
# exist. (See +assert_tag+ for a full discussion of the syntax.)
-
#
-
# === Examples
-
# # Assert that there is not a "div" containing a "p"
-
# assert_no_tag :tag => "div", :descendant => { :tag => "p" }
-
#
-
# # Assert that an unordered list is empty
-
# assert_no_tag :tag => "ul", :descendant => { :tag => "li" }
-
#
-
# # Assert that there is not a "p" tag with between 1 to 3 "img" tags
-
# # as immediate children
-
# assert_no_tag :tag => "p",
-
# :children => { :count => 1..3, :only => { :tag => "img" } }
-
1
def assert_no_tag(*opts)
-
opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
-
tag = find_tag(opts)
-
assert !tag, "expected no tag, but found tag matching #{opts.inspect} in:\n#{@response.body.inspect}"
-
end
-
-
1
def find_tag(conditions)
-
html_document.find(conditions)
-
end
-
-
1
def find_all_tag(conditions)
-
html_document.find_all(conditions)
-
end
-
-
1
def html_document
-
xml = @response.content_type =~ /xml$/
-
@html_document ||= HTML::Document.new(@response.body, false, xml)
-
end
-
end
-
end
-
end
-
1
require 'stringio'
-
1
require 'uri'
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
1
require 'active_support/core_ext/object/inclusion'
-
1
require 'active_support/core_ext/object/try'
-
1
require 'rack/test'
-
1
require 'test/unit/assertions'
-
-
1
module ActionDispatch
-
1
module Integration #:nodoc:
-
1
module RequestHelpers
-
# Performs a GET request with the given parameters.
-
#
-
# - +path+: The URI (as a String) on which you want to perform a GET
-
# request.
-
# - +parameters+: The HTTP parameters that you want to pass. This may
-
# be +nil+,
-
# a Hash, or a String that is appropriately encoded
-
# (<tt>application/x-www-form-urlencoded</tt> or
-
# <tt>multipart/form-data</tt>).
-
# - +headers+: Additional HTTP headers to pass, as a Hash. The keys will
-
# automatically be upcased, with the prefix 'HTTP_' added if needed.
-
#
-
# This method returns an Response object, which one can use to
-
# inspect the details of the response. Furthermore, if this method was
-
# called from an ActionDispatch::IntegrationTest object, then that
-
# object's <tt>@response</tt> instance variable will point to the same
-
# response object.
-
#
-
# You can also perform POST, PUT, DELETE, and HEAD requests with +#post+,
-
# +#put+, +#delete+, and +#head+.
-
1
def get(path, parameters = nil, headers = nil)
-
process :get, path, parameters, headers
-
end
-
-
# Performs a POST request with the given parameters. See +#get+ for more
-
# details.
-
1
def post(path, parameters = nil, headers = nil)
-
process :post, path, parameters, headers
-
end
-
-
# Performs a PUT request with the given parameters. See +#get+ for more
-
# details.
-
1
def put(path, parameters = nil, headers = nil)
-
process :put, path, parameters, headers
-
end
-
-
# Performs a DELETE request with the given parameters. See +#get+ for
-
# more details.
-
1
def delete(path, parameters = nil, headers = nil)
-
process :delete, path, parameters, headers
-
end
-
-
# Performs a HEAD request with the given parameters. See +#get+ for more
-
# details.
-
1
def head(path, parameters = nil, headers = nil)
-
process :head, path, parameters, headers
-
end
-
-
# Performs an XMLHttpRequest request with the given parameters, mirroring
-
# a request from the Prototype library.
-
#
-
# The request_method is +:get+, +:post+, +:put+, +:delete+ or +:head+; the
-
# parameters are +nil+, a hash, or a url-encoded or multipart string;
-
# the headers are a hash. Keys are automatically upcased and prefixed
-
# with 'HTTP_' if not already.
-
1
def xml_http_request(request_method, path, parameters = nil, headers = nil)
-
headers ||= {}
-
headers['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
-
headers['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
-
process(request_method, path, parameters, headers)
-
end
-
1
alias xhr :xml_http_request
-
-
# Follow a single redirect response. If the last response was not a
-
# redirect, an exception will be raised. Otherwise, the redirect is
-
# performed on the location header.
-
1
def follow_redirect!
-
raise "not a redirect! #{status} #{status_message}" unless redirect?
-
get(response.location)
-
status
-
end
-
-
# Performs a request using the specified method, following any subsequent
-
# redirect. Note that the redirects are followed until the response is
-
# not a redirect--this means you may run into an infinite loop if your
-
# redirect loops back to itself.
-
1
def request_via_redirect(http_method, path, parameters = nil, headers = nil)
-
process(http_method, path, parameters, headers)
-
follow_redirect! while redirect?
-
status
-
end
-
-
# Performs a GET request, following any subsequent redirect.
-
# See +request_via_redirect+ for more information.
-
1
def get_via_redirect(path, parameters = nil, headers = nil)
-
request_via_redirect(:get, path, parameters, headers)
-
end
-
-
# Performs a POST request, following any subsequent redirect.
-
# See +request_via_redirect+ for more information.
-
1
def post_via_redirect(path, parameters = nil, headers = nil)
-
request_via_redirect(:post, path, parameters, headers)
-
end
-
-
# Performs a PUT request, following any subsequent redirect.
-
# See +request_via_redirect+ for more information.
-
1
def put_via_redirect(path, parameters = nil, headers = nil)
-
request_via_redirect(:put, path, parameters, headers)
-
end
-
-
# Performs a DELETE request, following any subsequent redirect.
-
# See +request_via_redirect+ for more information.
-
1
def delete_via_redirect(path, parameters = nil, headers = nil)
-
request_via_redirect(:delete, path, parameters, headers)
-
end
-
end
-
-
# An instance of this class represents a set of requests and responses
-
# performed sequentially by a test process. Because you can instantiate
-
# multiple sessions and run them side-by-side, you can also mimic (to some
-
# limited extent) multiple simultaneous users interacting with your system.
-
#
-
# Typically, you will instantiate a new session using
-
# IntegrationTest#open_session, rather than instantiating
-
# Integration::Session directly.
-
1
class Session
-
1
DEFAULT_HOST = "www.example.com"
-
-
1
include Test::Unit::Assertions
-
1
include TestProcess, RequestHelpers, Assertions
-
-
1
%w( status status_message headers body redirect? ).each do |method|
-
5
delegate method, :to => :response, :allow_nil => true
-
end
-
-
1
%w( path ).each do |method|
-
1
delegate method, :to => :request, :allow_nil => true
-
end
-
-
# The hostname used in the last request.
-
1
def host
-
@host || DEFAULT_HOST
-
end
-
1
attr_writer :host
-
-
# The remote_addr used in the last request.
-
1
attr_accessor :remote_addr
-
-
# The Accept header to send.
-
1
attr_accessor :accept
-
-
# A map of the cookies returned by the last response, and which will be
-
# sent with the next request.
-
1
def cookies
-
_mock_session.cookie_jar
-
end
-
-
# A reference to the controller instance used by the last request.
-
1
attr_reader :controller
-
-
# A reference to the request instance used by the last request.
-
1
attr_reader :request
-
-
# A reference to the response instance used by the last request.
-
1
attr_reader :response
-
-
# A running counter of the number of requests processed.
-
1
attr_accessor :request_count
-
-
1
include ActionDispatch::Routing::UrlFor
-
-
# Create and initialize a new Session instance.
-
1
def initialize(app)
-
super()
-
@app = app
-
-
# If the app is a Rails app, make url_helpers available on the session
-
# This makes app.url_for and app.foo_path available in the console
-
if app.respond_to?(:routes) && app.routes.respond_to?(:url_helpers)
-
singleton_class.class_eval { include app.routes.url_helpers }
-
end
-
-
reset!
-
end
-
-
1
remove_method :default_url_options
-
1
def default_url_options
-
{ :host => host, :protocol => https? ? "https" : "http" }
-
end
-
-
# Resets the instance. This can be used to reset the state information
-
# in an existing session instance, so it can be used from a clean-slate
-
# condition.
-
#
-
# session.reset!
-
1
def reset!
-
@https = false
-
@controller = @request = @response = nil
-
@_mock_session = nil
-
@request_count = 0
-
-
self.host = DEFAULT_HOST
-
self.remote_addr = "127.0.0.1"
-
self.accept = "text/xml,application/xml,application/xhtml+xml," +
-
"text/html;q=0.9,text/plain;q=0.8,image/png," +
-
"*/*;q=0.5"
-
-
unless defined? @named_routes_configured
-
# install the named routes in this session instance.
-
klass = singleton_class
-
-
# the helpers are made protected by default--we make them public for
-
# easier access during testing and troubleshooting.
-
@named_routes_configured = true
-
end
-
end
-
-
# Specify whether or not the session should mimic a secure HTTPS request.
-
#
-
# session.https!
-
# session.https!(false)
-
1
def https!(flag = true)
-
@https = flag
-
end
-
-
# Return +true+ if the session is mimicking a secure HTTPS request.
-
#
-
# if session.https?
-
# ...
-
# end
-
1
def https?
-
@https
-
end
-
-
# Set the host name to use in the next request.
-
#
-
# session.host! "www.example.com"
-
1
alias :host! :host=
-
-
1
private
-
1
def _mock_session
-
@_mock_session ||= Rack::MockSession.new(@app, host)
-
end
-
-
# Performs the actual request.
-
1
def process(method, path, parameters = nil, env = nil)
-
env ||= {}
-
if path =~ %r{://}
-
location = URI.parse(path)
-
https! URI::HTTPS === location if location.scheme
-
host! location.host if location.host
-
path = location.query ? "#{location.path}?#{location.query}" : location.path
-
end
-
-
unless ActionController::Base < ActionController::Testing
-
ActionController::Base.class_eval do
-
include ActionController::Testing
-
end
-
end
-
-
hostname, port = host.split(':')
-
-
default_env = {
-
:method => method,
-
:params => parameters,
-
-
"SERVER_NAME" => hostname,
-
"SERVER_PORT" => port || (https? ? "443" : "80"),
-
"HTTPS" => https? ? "on" : "off",
-
"rack.url_scheme" => https? ? "https" : "http",
-
-
"REQUEST_URI" => path,
-
"HTTP_HOST" => host,
-
"REMOTE_ADDR" => remote_addr,
-
"CONTENT_TYPE" => "application/x-www-form-urlencoded",
-
"HTTP_ACCEPT" => accept
-
}
-
-
session = Rack::Test::Session.new(_mock_session)
-
-
env.reverse_merge!(default_env)
-
-
# NOTE: rack-test v0.5 doesn't build a default uri correctly
-
# Make sure requested path is always a full uri
-
uri = URI.parse('/')
-
uri.scheme ||= env['rack.url_scheme']
-
uri.host ||= env['SERVER_NAME']
-
uri.port ||= env['SERVER_PORT'].try(:to_i)
-
uri += path
-
-
session.request(uri.to_s, env)
-
-
@request_count += 1
-
@request = ActionDispatch::Request.new(session.last_request.env)
-
response = _mock_session.last_response
-
@response = ActionDispatch::TestResponse.new(response.status, response.headers, response.body)
-
@html_document = nil
-
-
@controller = session.last_request.env['action_controller.instance']
-
-
return response.status
-
end
-
end
-
-
1
module Runner
-
1
include ActionDispatch::Assertions
-
-
1
def app
-
@app ||= nil
-
end
-
-
# Reset the current session. This is useful for testing multiple sessions
-
# in a single test case.
-
1
def reset!
-
@integration_session = Integration::Session.new(app)
-
end
-
-
1
%w(get post put head delete cookies assigns
-
xml_http_request xhr get_via_redirect post_via_redirect).each do |method|
-
11
define_method(method) do |*args|
-
reset! unless integration_session
-
# reset the html_document variable, but only for new get/post calls
-
@html_document = nil unless method.in?(["cookies", "assigns"])
-
integration_session.__send__(method, *args).tap do
-
copy_session_variables!
-
end
-
end
-
end
-
-
# Open a new session instance. If a block is given, the new session is
-
# yielded to the block before being returned.
-
#
-
# session = open_session do |sess|
-
# sess.extend(CustomAssertions)
-
# end
-
#
-
# By default, a single session is automatically created for you, but you
-
# can use this method to open multiple sessions that ought to be tested
-
# simultaneously.
-
1
def open_session(app = nil)
-
dup.tap do |session|
-
yield session if block_given?
-
end
-
end
-
-
# Copy the instance variables from the current session instance into the
-
# test instance.
-
1
def copy_session_variables! #:nodoc:
-
return unless integration_session
-
%w(controller response request).each do |var|
-
instance_variable_set("@#{var}", @integration_session.__send__(var))
-
end
-
end
-
-
1
extend ActiveSupport::Concern
-
1
include ActionDispatch::Routing::UrlFor
-
-
1
def url_options
-
reset! unless integration_session
-
integration_session.url_options
-
end
-
-
1
def respond_to?(method, include_private = false)
-
integration_session.respond_to?(method, include_private) || super
-
end
-
-
# Delegate unhandled messages to the current session instance.
-
1
def method_missing(sym, *args, &block)
-
reset! unless integration_session
-
if integration_session.respond_to?(sym)
-
integration_session.__send__(sym, *args, &block).tap do
-
copy_session_variables!
-
end
-
else
-
super
-
end
-
end
-
-
1
private
-
1
def integration_session
-
@integration_session ||= nil
-
end
-
end
-
end
-
-
# An integration test spans multiple controllers and actions,
-
# tying them all together to ensure they work together as expected. It tests
-
# more completely than either unit or functional tests do, exercising the
-
# entire stack, from the dispatcher to the database.
-
#
-
# At its simplest, you simply extend <tt>IntegrationTest</tt> and write your tests
-
# using the get/post methods:
-
#
-
# require "test_helper"
-
#
-
# class ExampleTest < ActionDispatch::IntegrationTest
-
# fixtures :people
-
#
-
# def test_login
-
# # get the login page
-
# get "/login"
-
# assert_equal 200, status
-
#
-
# # post the login and follow through to the home page
-
# post "/login", :username => people(:jamis).username,
-
# :password => people(:jamis).password
-
# follow_redirect!
-
# assert_equal 200, status
-
# assert_equal "/home", path
-
# end
-
# end
-
#
-
# However, you can also have multiple session instances open per test, and
-
# even extend those instances with assertions and methods to create a very
-
# powerful testing DSL that is specific for your application. You can even
-
# reference any named routes you happen to have defined.
-
#
-
# require "test_helper"
-
#
-
# class AdvancedTest < ActionDispatch::IntegrationTest
-
# fixtures :people, :rooms
-
#
-
# def test_login_and_speak
-
# jamis, david = login(:jamis), login(:david)
-
# room = rooms(:office)
-
#
-
# jamis.enter(room)
-
# jamis.speak(room, "anybody home?")
-
#
-
# david.enter(room)
-
# david.speak(room, "hello!")
-
# end
-
#
-
# private
-
#
-
# module CustomAssertions
-
# def enter(room)
-
# # reference a named route, for maximum internal consistency!
-
# get(room_url(:id => room.id))
-
# assert(...)
-
# ...
-
# end
-
#
-
# def speak(room, message)
-
# xml_http_request "/say/#{room.id}", :message => message
-
# assert(...)
-
# ...
-
# end
-
# end
-
#
-
# def login(who)
-
# open_session do |sess|
-
# sess.extend(CustomAssertions)
-
# who = people(who)
-
# sess.post "/login", :username => who.username,
-
# :password => who.password
-
# assert(...)
-
# end
-
# end
-
# end
-
1
class IntegrationTest < ActiveSupport::TestCase
-
1
include Integration::Runner
-
1
include ActionController::TemplateAssertions
-
-
1
@@app = nil
-
-
1
def self.app
-
# DEPRECATE Rails application fallback
-
# This should be set by the initializer
-
@@app || (defined?(Rails.application) && Rails.application) || nil
-
end
-
-
1
def self.app=(app)
-
@@app = app
-
end
-
-
1
def app
-
super || self.class.app
-
end
-
end
-
end
-
1
require 'action_dispatch/middleware/flash'
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
-
1
module ActionDispatch
-
1
module TestProcess
-
1
def assigns(key = nil)
-
assigns = {}.with_indifferent_access
-
@controller.instance_variable_names.each do |ivar|
-
next if ActionController::Base.protected_instance_variables.include?(ivar)
-
assigns[ivar[1..-1]] = @controller.instance_variable_get(ivar)
-
end
-
-
key.nil? ? assigns : assigns[key]
-
end
-
-
1
def session
-
@request.session
-
end
-
-
1
def flash
-
@request.flash
-
end
-
-
1
def cookies
-
@request.cookies.merge(@response.cookies).with_indifferent_access
-
end
-
-
1
def redirect_to_url
-
@response.redirect_url
-
end
-
-
# Shortcut for <tt>Rack::Test::UploadedFile.new(ActionController::TestCase.fixture_path + path, type)</tt>:
-
#
-
# post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png')
-
#
-
# To upload binary files on Windows, pass <tt>:binary</tt> as the last parameter.
-
# This will not affect other platforms:
-
#
-
# post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png', :binary)
-
1
def fixture_file_upload(path, mime_type = nil, binary = false)
-
fixture_path = ActionController::TestCase.send(:fixture_path) if ActionController::TestCase.respond_to?(:fixture_path)
-
Rack::Test::UploadedFile.new("#{fixture_path}#{path}", mime_type, binary)
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/hash/reverse_merge'
-
1
require 'rack/utils'
-
-
1
module ActionDispatch
-
1
class TestRequest < Request
-
1
DEFAULT_ENV = Rack::MockRequest.env_for('/')
-
-
1
def self.new(env = {})
-
super
-
end
-
-
1
def initialize(env = {})
-
env = Rails.application.env_config.merge(env) if defined?(Rails.application)
-
super(DEFAULT_ENV.merge(env))
-
-
@cookies = nil
-
self.host = 'test.host'
-
self.remote_addr = '0.0.0.0'
-
self.user_agent = 'Rails Testing'
-
end
-
-
1
def env
-
write_cookies!
-
delete_nil_values!
-
super
-
end
-
-
1
def request_method=(method)
-
@env['REQUEST_METHOD'] = method.to_s.upcase
-
end
-
-
1
def host=(host)
-
@env['HTTP_HOST'] = host
-
end
-
-
1
def port=(number)
-
@env['SERVER_PORT'] = number.to_i
-
end
-
-
1
def request_uri=(uri)
-
@env['REQUEST_URI'] = uri
-
end
-
-
1
def path=(path)
-
@env['PATH_INFO'] = path
-
end
-
-
1
def action=(action_name)
-
path_parameters["action"] = action_name.to_s
-
end
-
-
1
def if_modified_since=(last_modified)
-
@env['HTTP_IF_MODIFIED_SINCE'] = last_modified
-
end
-
-
1
def if_none_match=(etag)
-
@env['HTTP_IF_NONE_MATCH'] = etag
-
end
-
-
1
def remote_addr=(addr)
-
@env['REMOTE_ADDR'] = addr
-
end
-
-
1
def user_agent=(user_agent)
-
@env['HTTP_USER_AGENT'] = user_agent
-
end
-
-
1
def accept=(mime_types)
-
@env.delete('action_dispatch.request.accepts')
-
@env['HTTP_ACCEPT'] = Array(mime_types).collect { |mime_type| mime_type.to_s }.join(",")
-
end
-
-
1
def cookies
-
@cookies ||= super
-
end
-
-
1
private
-
1
def write_cookies!
-
unless @cookies.blank?
-
@env['HTTP_COOKIE'] = @cookies.map { |name, value| escape_cookie(name, value) }.join('; ')
-
end
-
end
-
-
1
def escape_cookie(name, value)
-
"#{Rack::Utils.escape(name)}=#{Rack::Utils.escape(value.to_s)}"
-
end
-
-
1
def delete_nil_values!
-
@env.delete_if { |k, v| v.nil? }
-
end
-
end
-
end
-
1
module ActionDispatch
-
# Integration test methods such as ActionDispatch::Integration::Session#get
-
# and ActionDispatch::Integration::Session#post return objects of class
-
# TestResponse, which represent the HTTP response results of the requested
-
# controller actions.
-
#
-
# See Response for more information on controller response objects.
-
1
class TestResponse < Response
-
1
def self.from_response(response)
-
new.tap do |resp|
-
resp.status = response.status
-
resp.headers = response.headers
-
resp.body = response.body
-
end
-
end
-
-
# Was the response successful?
-
1
alias_method :success?, :successful?
-
-
# Was the URL not found?
-
1
alias_method :missing?, :not_found?
-
-
# Were we redirected?
-
1
alias_method :redirect?, :redirection?
-
-
# Was there a server-side error?
-
1
alias_method :error?, :server_error?
-
end
-
end
-
#--
-
# Copyright (c) 2004-2011 David Heinemeier Hansson
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining
-
# a copy of this software and associated documentation files (the
-
# "Software"), to deal in the Software without restriction, including
-
# without limitation the rights to use, copy, modify, merge, publish,
-
# distribute, sublicense, and/or sell copies of the Software, and to
-
# permit persons to whom the Software is furnished to do so, subject to
-
# the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be
-
# included in all copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
#++
-
-
1
require 'active_support/ruby/shim'
-
1
require 'active_support/core_ext/class/attribute_accessors'
-
-
1
require 'action_pack'
-
-
1
module ActionView
-
1
extend ActiveSupport::Autoload
-
-
1
eager_autoload do
-
1
autoload :AssetPaths
-
1
autoload :Base
-
1
autoload :Context
-
1
autoload :Helpers
-
1
autoload :LookupContext
-
1
autoload :PathSet
-
1
autoload :Template
-
1
autoload :TestCase
-
-
1
autoload_under "renderer" do
-
1
autoload :Renderer
-
1
autoload :AbstractRenderer
-
1
autoload :PartialRenderer
-
1
autoload :TemplateRenderer
-
1
autoload :StreamingTemplateRenderer
-
end
-
-
1
autoload_at "action_view/template/resolver" do
-
1
autoload :Resolver
-
1
autoload :PathResolver
-
1
autoload :FileSystemResolver
-
1
autoload :OptimizedFileSystemResolver
-
1
autoload :FallbackFileSystemResolver
-
end
-
-
1
autoload_at "action_view/buffers" do
-
1
autoload :OutputBuffer
-
1
autoload :StreamingBuffer
-
end
-
-
1
autoload_at "action_view/flows" do
-
1
autoload :OutputFlow
-
1
autoload :StreamingFlow
-
end
-
-
1
autoload_at "action_view/template/error" do
-
1
autoload :MissingTemplate
-
1
autoload :ActionViewError
-
1
autoload :EncodingError
-
1
autoload :TemplateError
-
1
autoload :WrongEncodingError
-
end
-
-
1
autoload_at "action_view/template" do
-
1
autoload :TemplateHandler
-
1
autoload :TemplateHandlers
-
end
-
end
-
-
1
ENCODING_FLAG = '#.*coding[:=]\s*(\S+)[ \t]*'
-
end
-
-
1
require 'active_support/i18n'
-
1
require 'active_support/core_ext/string/output_safety'
-
-
1
I18n.load_path << "#{File.dirname(__FILE__)}/action_view/locale/en.yml"
-
1
require 'zlib'
-
1
require 'active_support/core_ext/file'
-
-
1
module ActionView
-
-
1
class AssetPaths #:nodoc:
-
1
attr_reader :config, :controller
-
-
1
def initialize(config, controller = nil)
-
@config = config
-
@controller = controller
-
end
-
-
# Add the extension +ext+ if not present. Return full or scheme-relative URLs otherwise untouched.
-
# Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL
-
# roots. Rewrite the asset path for cache-busting asset ids. Include
-
# asset host, if configured, with the correct request protocol.
-
#
-
# When include_host is true and the asset host does not specify the protocol
-
# the protocol parameter specifies how the protocol will be added.
-
# When :relative (default), the protocol will be determined by the client using current protocol
-
# When :request, the protocol will be the request protocol
-
# Otherwise, the protocol is used (E.g. :http, :https, etc)
-
1
def compute_public_path(source, dir, ext = nil, include_host = true, protocol = nil)
-
source = source.to_s
-
return source if is_uri?(source)
-
-
source = rewrite_extension(source, dir, ext) if ext
-
source = rewrite_asset_path(source, dir)
-
source = rewrite_relative_url_root(source, relative_url_root) if has_request?
-
source = rewrite_host_and_protocol(source, protocol) if include_host
-
source
-
end
-
-
# Return the filesystem path for the source
-
1
def compute_source_path(source, dir, ext)
-
source = rewrite_extension(source, dir, ext) if ext
-
File.join(config.assets_dir, dir, source)
-
end
-
-
1
def is_uri?(path)
-
path =~ %r{^[-a-z]+://|^cid:|^//}
-
end
-
-
1
private
-
-
1
def rewrite_extension(source, dir, ext)
-
raise NotImplementedError
-
end
-
-
1
def rewrite_asset_path(source, path = nil)
-
raise NotImplementedError
-
end
-
-
1
def rewrite_relative_url_root(source, relative_url_root)
-
relative_url_root && !source.starts_with?("#{relative_url_root}/") ? "#{relative_url_root}#{source}" : source
-
end
-
-
1
def has_request?
-
controller.respond_to?(:request)
-
end
-
-
1
def rewrite_host_and_protocol(source, protocol = nil)
-
host = compute_asset_host(source)
-
if host && !is_uri?(host)
-
if (protocol || default_protocol) == :request && !has_request?
-
host = nil
-
else
-
host = "#{compute_protocol(protocol)}#{host}"
-
end
-
end
-
host.nil? ? source : "#{host}#{source}"
-
end
-
-
1
def compute_protocol(protocol)
-
protocol ||= default_protocol
-
case protocol
-
when :relative
-
"//"
-
when :request
-
unless @controller
-
invalid_asset_host!("The protocol requested was :request. Consider using :relative instead.")
-
end
-
@controller.request.protocol
-
else
-
"#{protocol}://"
-
end
-
end
-
-
1
def default_protocol
-
protocol = @config.action_controller.default_asset_host_protocol if @config.action_controller.present?
-
protocol ||= @config.default_asset_host_protocol
-
protocol || (has_request? ? :request : :relative)
-
end
-
-
1
def invalid_asset_host!(help_message)
-
raise ActionController::RoutingError, "This asset host cannot be computed without a request in scope. #{help_message}"
-
end
-
-
# Pick an asset host for this source. Returns +nil+ if no host is set,
-
# the host if no wildcard is set, the host interpolated with the
-
# numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4),
-
# or the value returned from invoking the proc if it's a proc or the value from
-
# invoking call if it's an object responding to call.
-
1
def compute_asset_host(source)
-
if host = asset_host_config
-
if host.respond_to?(:call)
-
args = [source]
-
arity = arity_of(host)
-
if arity > 1 && !has_request?
-
invalid_asset_host!("Remove the second argument to your asset_host Proc if you do not need the request.")
-
end
-
args << current_request if (arity > 1 || arity < 0) && has_request?
-
host.call(*args)
-
else
-
(host =~ /%d/) ? host % (Zlib.crc32(source) % 4) : host
-
end
-
end
-
end
-
-
1
def relative_url_root
-
config = controller.config if controller.respond_to?(:config)
-
config ||= config.action_controller if config.action_controller.present?
-
config ||= config
-
config.relative_url_root
-
end
-
-
1
def asset_host_config
-
if config.action_controller.present?
-
config.action_controller.asset_host
-
else
-
config.asset_host
-
end
-
end
-
-
# Returns the current request if one exists.
-
1
def current_request
-
controller.request if has_request?
-
end
-
-
# Returns the arity of a callable
-
1
def arity_of(callable)
-
callable.respond_to?(:arity) ? callable.arity : callable.method(:call).arity
-
end
-
-
end
-
-
end
-
1
require 'active_support/core_ext/module/attr_internal'
-
1
require 'active_support/core_ext/module/delegation'
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_support/ordered_options'
-
1
require 'action_view/log_subscriber'
-
-
1
module ActionView #:nodoc:
-
# = Action View Base
-
#
-
# Action View templates can be written in several ways. If the template file has a <tt>.erb</tt> (or <tt>.rhtml</tt>) extension then it uses a mixture of ERb
-
# (included in Ruby) and HTML. If the template file has a <tt>.builder</tt> (or <tt>.rxml</tt>) extension then Jim Weirich's Builder::XmlMarkup library is used.
-
#
-
# == ERB
-
#
-
# You trigger ERB by using embeddings such as <% %>, <% -%>, and <%= %>. The <%= %> tag set is used when you want output. Consider the
-
# following loop for names:
-
#
-
# <b>Names of all the people</b>
-
# <% @people.each do |person| %>
-
# Name: <%= person.name %><br/>
-
# <% end %>
-
#
-
# The loop is setup in regular embedding tags <% %> and the name is written using the output embedding tag <%= %>. Note that this
-
# is not just a usage suggestion. Regular output functions like print or puts won't work with ERB templates. So this would be wrong:
-
#
-
# <%# WRONG %>
-
# Hi, Mr. <% puts "Frodo" %>
-
#
-
# If you absolutely must write from within a function use +concat+.
-
#
-
# <%- and -%> suppress leading and trailing whitespace, including the trailing newline, and can be used interchangeably with <% and %>.
-
#
-
# === Using sub templates
-
#
-
# Using sub templates allows you to sidestep tedious replication and extract common display structures in shared templates. The
-
# classic example is the use of a header and footer (even though the Action Pack-way would be to use Layouts):
-
#
-
# <%= render "shared/header" %>
-
# Something really specific and terrific
-
# <%= render "shared/footer" %>
-
#
-
# As you see, we use the output embeddings for the render methods. The render call itself will just return a string holding the
-
# result of the rendering. The output embedding writes it to the current template.
-
#
-
# But you don't have to restrict yourself to static includes. Templates can share variables amongst themselves by using instance
-
# variables defined using the regular embedding tags. Like this:
-
#
-
# <% @page_title = "A Wonderful Hello" %>
-
# <%= render "shared/header" %>
-
#
-
# Now the header can pick up on the <tt>@page_title</tt> variable and use it for outputting a title tag:
-
#
-
# <title><%= @page_title %></title>
-
#
-
# === Passing local variables to sub templates
-
#
-
# You can pass local variables to sub templates by using a hash with the variable names as keys and the objects as values:
-
#
-
# <%= render "shared/header", { :headline => "Welcome", :person => person } %>
-
#
-
# These can now be accessed in <tt>shared/header</tt> with:
-
#
-
# Headline: <%= headline %>
-
# First name: <%= person.first_name %>
-
#
-
# If you need to find out whether a certain local variable has been assigned a value in a particular render call,
-
# you need to use the following pattern:
-
#
-
# <% if local_assigns.has_key? :headline %>
-
# Headline: <%= headline %>
-
# <% end %>
-
#
-
# Testing using <tt>defined? headline</tt> will not work. This is an implementation restriction.
-
#
-
# === Template caching
-
#
-
# By default, Rails will compile each template to a method in order to render it. When you alter a template,
-
# Rails will check the file's modification time and recompile it in development mode.
-
#
-
# == Builder
-
#
-
# Builder templates are a more programmatic alternative to ERB. They are especially useful for generating XML content. An XmlMarkup object
-
# named +xml+ is automatically made available to templates with a <tt>.builder</tt> extension.
-
#
-
# Here are some basic examples:
-
#
-
# xml.em("emphasized") # => <em>emphasized</em>
-
# xml.em { xml.b("emph & bold") } # => <em><b>emph & bold</b></em>
-
# xml.a("A Link", "href" => "http://onestepback.org") # => <a href="http://onestepback.org">A Link</a>
-
# xml.target("name" => "compile", "option" => "fast") # => <target option="fast" name="compile"\>
-
# # NOTE: order of attributes is not specified.
-
#
-
# Any method with a block will be treated as an XML markup tag with nested markup in the block. For example, the following:
-
#
-
# xml.div {
-
# xml.h1(@person.name)
-
# xml.p(@person.bio)
-
# }
-
#
-
# would produce something like:
-
#
-
# <div>
-
# <h1>David Heinemeier Hansson</h1>
-
# <p>A product of Danish Design during the Winter of '79...</p>
-
# </div>
-
#
-
# A full-length RSS example actually used on Basecamp:
-
#
-
# xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
-
# xml.channel do
-
# xml.title(@feed_title)
-
# xml.link(@url)
-
# xml.description "Basecamp: Recent items"
-
# xml.language "en-us"
-
# xml.ttl "40"
-
#
-
# for item in @recent_items
-
# xml.item do
-
# xml.title(item_title(item))
-
# xml.description(item_description(item)) if item_description(item)
-
# xml.pubDate(item_pubDate(item))
-
# xml.guid(@person.firm.account.url + @recent_items.url(item))
-
# xml.link(@person.firm.account.url + @recent_items.url(item))
-
#
-
# xml.tag!("dc:creator", item.author_name) if item_has_creator?(item)
-
# end
-
# end
-
# end
-
# end
-
#
-
# More builder documentation can be found at http://builder.rubyforge.org.
-
1
class Base
-
1
include Helpers, ::ERB::Util, Context
-
-
# Specify the proc used to decorate input tags that refer to attributes with errors.
-
1
cattr_accessor :field_error_proc
-
1
@@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe }
-
-
# How to complete the streaming when an exception occurs.
-
# This is our best guess: first try to close the attribute, then the tag.
-
1
cattr_accessor :streaming_completion_on_exception
-
1
@@streaming_completion_on_exception = %("><script type="text/javascript">window.location = "/500.html"</script></html>)
-
-
1
class_attribute :helpers
-
1
class_attribute :_routes
-
-
1
class << self
-
1
delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB'
-
1
delegate :logger, :to => 'ActionController::Base', :allow_nil => true
-
-
1
def cache_template_loading
-
ActionView::Resolver.caching?
-
end
-
-
1
def cache_template_loading=(value)
-
ActionView::Resolver.caching = value
-
end
-
-
1
def process_view_paths(value)
-
1
value.is_a?(PathSet) ?
-
1
value.dup : ActionView::PathSet.new(Array.wrap(value))
-
end
-
-
1
def xss_safe? #:nodoc:
-
true
-
end
-
-
# This method receives routes and helpers from the controller
-
# and return a subclass ready to be used as view context.
-
1
def prepare(routes, helpers) #:nodoc:
-
Class.new(self) do
-
if routes
-
include routes.url_helpers
-
include routes.mounted_helpers
-
end
-
-
if helpers
-
include helpers
-
self.helpers = helpers
-
end
-
end
-
end
-
end
-
-
1
attr_accessor :view_renderer
-
1
attr_internal :config, :assigns
-
-
1
delegate :lookup_context, :to => :view_renderer
-
1
delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, :to => :lookup_context
-
-
1
def assign(new_assigns) # :nodoc:
-
@_assigns = new_assigns.each { |key, value| instance_variable_set("@#{key}", value) }
-
end
-
-
1
def initialize(context = nil, assigns = {}, controller = nil, formats = nil) #:nodoc:
-
@_config = ActiveSupport::InheritableOptions.new
-
-
# Handle all these for backwards compatibility.
-
# TODO Provide a new API for AV::Base and deprecate this one.
-
if context.is_a?(ActionView::Renderer)
-
@view_renderer = context
-
elsif
-
lookup_context = context.is_a?(ActionView::LookupContext) ?
-
context : ActionView::LookupContext.new(context)
-
lookup_context.formats = formats if formats
-
lookup_context.prefixes = controller._prefixes if controller
-
@view_renderer = ActionView::Renderer.new(lookup_context)
-
end
-
-
assign(assigns)
-
assign_controller(controller)
-
_prepare_context
-
end
-
-
1
ActiveSupport.run_load_hooks(:action_view, self)
-
end
-
end
-
1
module ActionView
-
1
module CompiledTemplates #:nodoc:
-
# holds compiled template code
-
end
-
-
# = Action View Context
-
#
-
# Action View contexts are supplied to Action Controller to render template.
-
# The default Action View context is ActionView::Base.
-
#
-
# In order to work with ActionController, a Context must just include this module.
-
# The initialization of the variables used by the context (@output_buffer, @view_flow,
-
# and @virtual_path) is responsibility of the object that includes this module
-
# (although you can call _prepare_context defined below).
-
1
module Context
-
1
include CompiledTemplates
-
1
attr_accessor :output_buffer, :view_flow
-
-
# Prepares the context by setting the appropriate instance variables.
-
# :api: plugin
-
1
def _prepare_context
-
@view_flow = OutputFlow.new
-
@output_buffer = nil
-
@virtual_path = nil
-
end
-
-
# Encapsulates the interaction with the view flow so it
-
# returns the correct buffer on yield. This is usually
-
# overwriten by helpers to add more behavior.
-
# :api: plugin
-
1
def _layout_for(name=nil)
-
name ||= :layout
-
view_flow.get(name).html_safe
-
end
-
end
-
end
-
1
require 'active_support/benchmarkable'
-
-
1
module ActionView #:nodoc:
-
1
module Helpers #:nodoc:
-
1
extend ActiveSupport::Autoload
-
-
1
autoload :ActiveModelHelper
-
1
autoload :AssetTagHelper
-
1
autoload :AtomFeedHelper
-
1
autoload :CacheHelper
-
1
autoload :CaptureHelper
-
1
autoload :ControllerHelper
-
1
autoload :CsrfHelper
-
1
autoload :DateHelper
-
1
autoload :DebugHelper
-
1
autoload :FormHelper
-
1
autoload :FormOptionsHelper
-
1
autoload :FormTagHelper
-
1
autoload :JavaScriptHelper, "action_view/helpers/javascript_helper"
-
1
autoload :NumberHelper
-
1
autoload :OutputSafetyHelper
-
1
autoload :RecordTagHelper
-
1
autoload :RenderingHelper
-
1
autoload :SanitizeHelper
-
1
autoload :TagHelper
-
1
autoload :TextHelper
-
1
autoload :TranslationHelper
-
1
autoload :UrlHelper
-
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
extend SanitizeHelper::ClassMethods
-
end
-
-
1
include ActiveSupport::Benchmarkable
-
1
include ActiveModelHelper
-
1
include AssetTagHelper
-
1
include AtomFeedHelper
-
1
include CacheHelper
-
1
include CaptureHelper
-
1
include ControllerHelper
-
1
include CsrfHelper
-
1
include DateHelper
-
1
include DebugHelper
-
1
include FormHelper
-
1
include FormOptionsHelper
-
1
include FormTagHelper
-
1
include JavaScriptHelper
-
1
include NumberHelper
-
1
include OutputSafetyHelper
-
1
include RecordTagHelper
-
1
include RenderingHelper
-
1
include SanitizeHelper
-
1
include TagHelper
-
1
include TextHelper
-
1
include TranslationHelper
-
1
include UrlHelper
-
end
-
end
-
1
require 'action_view/helpers/form_helper'
-
1
require 'active_support/core_ext/class/attribute_accessors'
-
1
require 'active_support/core_ext/enumerable'
-
1
require 'active_support/core_ext/object/blank'
-
-
1
module ActionView
-
# = Active Model Helpers
-
1
module Helpers
-
1
module ActiveModelHelper
-
end
-
-
1
module ActiveModelInstanceTag
-
1
def object
-
@active_model_object ||= begin
-
object = super
-
object.respond_to?(:to_model) ? object.to_model : object
-
end
-
end
-
-
1
%w(content_tag to_date_select_tag to_datetime_select_tag to_time_select_tag).each do |meth|
-
4
module_eval "def #{meth}(*) error_wrapping(super) end", __FILE__, __LINE__
-
end
-
-
1
def tag(type, options, *)
-
tag_generate_errors?(options) ? error_wrapping(super) : super
-
end
-
-
1
def error_wrapping(html_tag)
-
if object_has_errors?
-
Base.field_error_proc.call(html_tag, self)
-
else
-
html_tag
-
end
-
end
-
-
1
def error_message
-
object.errors[@method_name]
-
end
-
-
1
private
-
-
1
def object_has_errors?
-
object.respond_to?(:errors) && object.errors.respond_to?(:full_messages) && error_message.any?
-
end
-
-
1
def tag_generate_errors?(options)
-
options['type'] != 'hidden'
-
end
-
end
-
-
1
class InstanceTag
-
1
include ActiveModelInstanceTag
-
end
-
end
-
end
-
1
require 'action_view/helpers/asset_tag_helpers/javascript_tag_helpers'
-
1
require 'action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers'
-
1
require 'action_view/helpers/asset_tag_helpers/asset_paths'
-
1
require 'action_view/helpers/tag_helper'
-
-
1
module ActionView
-
# = Action View Asset Tag Helpers
-
1
module Helpers #:nodoc:
-
# This module provides methods for generating HTML that links views to assets such
-
# as images, javascripts, stylesheets, and feeds. These methods do not verify
-
# the assets exist before linking to them:
-
#
-
# image_tag("rails.png")
-
# # => <img alt="Rails" src="/images/rails.png?1230601161" />
-
# stylesheet_link_tag("application")
-
# # => <link href="/stylesheets/application.css?1232285206" media="screen" rel="stylesheet" type="text/css" />
-
#
-
# === Using asset hosts
-
#
-
# By default, Rails links to these assets on the current host in the public
-
# folder, but you can direct Rails to link to assets from a dedicated asset
-
# server by setting ActionController::Base.asset_host in the application
-
# configuration, typically in <tt>config/environments/production.rb</tt>.
-
# For example, you'd define <tt>assets.example.com</tt> to be your asset
-
# host this way:
-
#
-
# ActionController::Base.asset_host = "assets.example.com"
-
#
-
# Helpers take that into account:
-
#
-
# image_tag("rails.png")
-
# # => <img alt="Rails" src="http://assets.example.com/images/rails.png?1230601161" />
-
# stylesheet_link_tag("application")
-
# # => <link href="http://assets.example.com/stylesheets/application.css?1232285206" media="screen" rel="stylesheet" type="text/css" />
-
#
-
# Browsers typically open at most two simultaneous connections to a single
-
# host, which means your assets often have to wait for other assets to finish
-
# downloading. You can alleviate this by using a <tt>%d</tt> wildcard in the
-
# +asset_host+. For example, "assets%d.example.com". If that wildcard is
-
# present Rails distributes asset requests among the corresponding four hosts
-
# "assets0.example.com", ..., "assets3.example.com". With this trick browsers
-
# will open eight simultaneous connections rather than two.
-
#
-
# image_tag("rails.png")
-
# # => <img alt="Rails" src="http://assets0.example.com/images/rails.png?1230601161" />
-
# stylesheet_link_tag("application")
-
# # => <link href="http://assets2.example.com/stylesheets/application.css?1232285206" media="screen" rel="stylesheet" type="text/css" />
-
#
-
# To do this, you can either setup four actual hosts, or you can use wildcard
-
# DNS to CNAME the wildcard to a single asset host. You can read more about
-
# setting up your DNS CNAME records from your ISP.
-
#
-
# Note: This is purely a browser performance optimization and is not meant
-
# for server load balancing. See http://www.die.net/musings/page_load_time/
-
# for background.
-
#
-
# Alternatively, you can exert more control over the asset host by setting
-
# +asset_host+ to a proc like this:
-
#
-
# ActionController::Base.asset_host = Proc.new { |source|
-
# "http://assets#{Digest::MD5.hexdigest(source).to_i(16) % 2 + 1}.example.com"
-
# }
-
# image_tag("rails.png")
-
# # => <img alt="Rails" src="http://assets1.example.com/images/rails.png?1230601161" />
-
# stylesheet_link_tag("application")
-
# # => <link href="http://assets2.example.com/stylesheets/application.css?1232285206" media="screen" rel="stylesheet" type="text/css" />
-
#
-
# The example above generates "http://assets1.example.com" and
-
# "http://assets2.example.com". This option is useful for example if
-
# you need fewer/more than four hosts, custom host names, etc.
-
#
-
# As you see the proc takes a +source+ parameter. That's a string with the
-
# absolute path of the asset with any extensions and timestamps in place,
-
# for example "/images/rails.png?1230601161".
-
#
-
# ActionController::Base.asset_host = Proc.new { |source|
-
# if source.starts_with?('/images')
-
# "http://images.example.com"
-
# else
-
# "http://assets.example.com"
-
# end
-
# }
-
# image_tag("rails.png")
-
# # => <img alt="Rails" src="http://images.example.com/images/rails.png?1230601161" />
-
# stylesheet_link_tag("application")
-
# # => <link href="http://assets.example.com/stylesheets/application.css?1232285206" media="screen" rel="stylesheet" type="text/css" />
-
#
-
# Alternatively you may ask for a second parameter +request+. That one is
-
# particularly useful for serving assets from an SSL-protected page. The
-
# example proc below disables asset hosting for HTTPS connections, while
-
# still sending assets for plain HTTP requests from asset hosts. If you don't
-
# have SSL certificates for each of the asset hosts this technique allows you
-
# to avoid warnings in the client about mixed media.
-
#
-
# ActionController::Base.asset_host = Proc.new { |source, request|
-
# if request.ssl?
-
# "#{request.protocol}#{request.host_with_port}"
-
# else
-
# "#{request.protocol}assets.example.com"
-
# end
-
# }
-
#
-
# You can also implement a custom asset host object that responds to +call+
-
# and takes either one or two parameters just like the proc.
-
#
-
# config.action_controller.asset_host = AssetHostingWithMinimumSsl.new(
-
# "http://asset%d.example.com", "https://asset1.example.com"
-
# )
-
#
-
# === Customizing the asset path
-
#
-
# By default, Rails appends asset's timestamps to all asset paths. This allows
-
# you to set a cache-expiration date for the asset far into the future, but
-
# still be able to instantly invalidate it by simply updating the file (and
-
# hence updating the timestamp, which then updates the URL as the timestamp
-
# is part of that, which in turn busts the cache).
-
#
-
# It's the responsibility of the web server you use to set the far-future
-
# expiration date on cache assets that you need to take advantage of this
-
# feature. Here's an example for Apache:
-
#
-
# # Asset Expiration
-
# ExpiresActive On
-
# <FilesMatch "\.(ico|gif|jpe?g|png|js|css)$">
-
# ExpiresDefault "access plus 1 year"
-
# </FilesMatch>
-
#
-
# Also note that in order for this to work, all your application servers must
-
# return the same timestamps. This means that they must have their clocks
-
# synchronized. If one of them drifts out of sync, you'll see different
-
# timestamps at random and the cache won't work. In that case the browser
-
# will request the same assets over and over again even thought they didn't
-
# change. You can use something like Live HTTP Headers for Firefox to verify
-
# that the cache is indeed working.
-
#
-
# This strategy works well enough for most server setups and requires the
-
# least configuration, but if you deploy several application servers at
-
# different times - say to handle a temporary spike in load - then the
-
# asset time stamps will be out of sync. In a setup like this you may want
-
# to set the way that asset paths are generated yourself.
-
#
-
# Altering the asset paths that Rails generates can be done in two ways.
-
# The easiest is to define the RAILS_ASSET_ID environment variable. The
-
# contents of this variable will always be used in preference to
-
# calculated timestamps. A more complex but flexible way is to set
-
# <tt>ActionController::Base.config.asset_path</tt> to a proc
-
# that takes the unmodified asset path and returns the path needed for
-
# your asset caching to work. Typically you'd do something like this in
-
# <tt>config/environments/production.rb</tt>:
-
#
-
# # Normally you'd calculate RELEASE_NUMBER at startup.
-
# RELEASE_NUMBER = 12345
-
# config.action_controller.asset_path = proc { |asset_path|
-
# "/release-#{RELEASE_NUMBER}#{asset_path}"
-
# }
-
#
-
# This example would cause the following behavior on all servers no
-
# matter when they were deployed:
-
#
-
# image_tag("rails.png")
-
# # => <img alt="Rails" src="/release-12345/images/rails.png" />
-
# stylesheet_link_tag("application")
-
# # => <link href="/release-12345/stylesheets/application.css?1232285206" media="screen" rel="stylesheet" type="text/css" />
-
#
-
# Changing the asset_path does require that your web servers have
-
# knowledge of the asset template paths that you rewrite to so it's not
-
# suitable for out-of-the-box use. To use the example given above you
-
# could use something like this in your Apache VirtualHost configuration:
-
#
-
# <LocationMatch "^/release-\d+/(images|javascripts|stylesheets)/.*$">
-
# # Some browsers still send conditional-GET requests if there's a
-
# # Last-Modified header or an ETag header even if they haven't
-
# # reached the expiry date sent in the Expires header.
-
# Header unset Last-Modified
-
# Header unset ETag
-
# FileETag None
-
#
-
# # Assets requested using a cache-busting filename should be served
-
# # only once and then cached for a really long time. The HTTP/1.1
-
# # spec frowns on hugely-long expiration times though and suggests
-
# # that assets which never expire be served with an expiration date
-
# # 1 year from access.
-
# ExpiresActive On
-
# ExpiresDefault "access plus 1 year"
-
# </LocationMatch>
-
#
-
# # We use cached-busting location names with the far-future expires
-
# # headers to ensure that if a file does change it can force a new
-
# # request. The actual asset filenames are still the same though so we
-
# # need to rewrite the location from the cache-busting location to the
-
# # real asset location so that we can serve it.
-
# RewriteEngine On
-
# RewriteRule ^/release-\d+/(images|javascripts|stylesheets)/(.*)$ /$1/$2 [L]
-
1
module AssetTagHelper
-
1
include TagHelper
-
1
include JavascriptTagHelpers
-
1
include StylesheetTagHelpers
-
# Returns a link tag that browsers and news readers can use to auto-detect
-
# an RSS or ATOM feed. The +type+ can either be <tt>:rss</tt> (default) or
-
# <tt>:atom</tt>. Control the link options in url_for format using the
-
# +url_options+. You can modify the LINK tag itself in +tag_options+.
-
#
-
# ==== Options
-
# * <tt>:rel</tt> - Specify the relation of this link, defaults to "alternate"
-
# * <tt>:type</tt> - Override the auto-generated mime type
-
# * <tt>:title</tt> - Specify the title of the link, defaults to the +type+
-
#
-
# ==== Examples
-
# auto_discovery_link_tag # =>
-
# <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/action" />
-
# auto_discovery_link_tag(:atom) # =>
-
# <link rel="alternate" type="application/atom+xml" title="ATOM" href="http://www.currenthost.com/controller/action" />
-
# auto_discovery_link_tag(:rss, {:action => "feed"}) # =>
-
# <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/feed" />
-
# auto_discovery_link_tag(:rss, {:action => "feed"}, {:title => "My RSS"}) # =>
-
# <link rel="alternate" type="application/rss+xml" title="My RSS" href="http://www.currenthost.com/controller/feed" />
-
# auto_discovery_link_tag(:rss, {:controller => "news", :action => "feed"}) # =>
-
# <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/news/feed" />
-
# auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", {:title => "Example RSS"}) # =>
-
# <link rel="alternate" type="application/rss+xml" title="Example RSS" href="http://www.example.com/feed" />
-
1
def auto_discovery_link_tag(type = :rss, url_options = {}, tag_options = {})
-
tag(
-
"link",
-
"rel" => tag_options[:rel] || "alternate",
-
"type" => tag_options[:type] || Mime::Type.lookup_by_extension(type.to_s).to_s,
-
"title" => tag_options[:title] || type.to_s.upcase,
-
"href" => url_options.is_a?(Hash) ? url_for(url_options.merge(:only_path => false)) : url_options
-
)
-
end
-
-
# Web browsers cache favicons. If you just throw a <tt>favicon.ico</tt> into the document
-
# root of your application and it changes later, clients that have it in their cache
-
# won't see the update. Using this helper prevents that because it appends an asset ID:
-
#
-
# <%= favicon_link_tag %>
-
#
-
# generates
-
#
-
# <link href="/favicon.ico?4649789979" rel="shortcut icon" type="image/vnd.microsoft.icon" />
-
#
-
# You may specify a different file in the first argument:
-
#
-
# <%= favicon_link_tag 'favicon.ico' %>
-
#
-
# That's passed to +path_to_image+ as is, so it gives
-
#
-
# <link href="/images/favicon.ico?4649789979" rel="shortcut icon" type="image/vnd.microsoft.icon" />
-
#
-
# The helper accepts an additional options hash where you can override "rel" and "type".
-
#
-
# For example, Mobile Safari looks for a different LINK tag, pointing to an image that
-
# will be used if you add the page to the home screen of an iPod Touch, iPhone, or iPad.
-
# The following call would generate such a tag:
-
#
-
# <%= favicon_link_tag 'mb-icon.png', :rel => 'apple-touch-icon', :type => 'image/png' %>
-
#
-
1
def favicon_link_tag(source='/favicon.ico', options={})
-
tag('link', {
-
:rel => 'shortcut icon',
-
:type => 'image/vnd.microsoft.icon',
-
:href => path_to_image(source)
-
}.merge(options.symbolize_keys))
-
end
-
-
# Computes the path to an image asset in the public images directory.
-
# Full paths from the document root will be passed through.
-
# Used internally by +image_tag+ to build the image path:
-
#
-
# image_path("edit") # => "/images/edit"
-
# image_path("edit.png") # => "/images/edit.png"
-
# image_path("icons/edit.png") # => "/images/icons/edit.png"
-
# image_path("/icons/edit.png") # => "/icons/edit.png"
-
# image_path("http://www.example.com/img/edit.png") # => "http://www.example.com/img/edit.png"
-
#
-
# If you have images as application resources this method may conflict with their named routes.
-
# The alias +path_to_image+ is provided to avoid that. Rails uses the alias internally, and
-
# plugin authors are encouraged to do so.
-
1
def image_path(source)
-
asset_paths.compute_public_path(source, 'images')
-
end
-
1
alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route
-
-
# Computes the path to a video asset in the public videos directory.
-
# Full paths from the document root will be passed through.
-
# Used internally by +video_tag+ to build the video path.
-
#
-
# ==== Examples
-
# video_path("hd") # => /videos/hd
-
# video_path("hd.avi") # => /videos/hd.avi
-
# video_path("trailers/hd.avi") # => /videos/trailers/hd.avi
-
# video_path("/trailers/hd.avi") # => /trailers/hd.avi
-
# video_path("http://www.example.com/vid/hd.avi") # => http://www.example.com/vid/hd.avi
-
1
def video_path(source)
-
asset_paths.compute_public_path(source, 'videos')
-
end
-
1
alias_method :path_to_video, :video_path # aliased to avoid conflicts with a video_path named route
-
-
# Computes the path to an audio asset in the public audios directory.
-
# Full paths from the document root will be passed through.
-
# Used internally by +audio_tag+ to build the audio path.
-
#
-
# ==== Examples
-
# audio_path("horse") # => /audios/horse
-
# audio_path("horse.wav") # => /audios/horse.wav
-
# audio_path("sounds/horse.wav") # => /audios/sounds/horse.wav
-
# audio_path("/sounds/horse.wav") # => /sounds/horse.wav
-
# audio_path("http://www.example.com/sounds/horse.wav") # => http://www.example.com/sounds/horse.wav
-
1
def audio_path(source)
-
asset_paths.compute_public_path(source, 'audios')
-
end
-
1
alias_method :path_to_audio, :audio_path # aliased to avoid conflicts with an audio_path named route
-
-
# Returns an html image tag for the +source+. The +source+ can be a full
-
# path or a file that exists in your public images directory.
-
#
-
# ==== Options
-
# You can add HTML attributes using the +options+. The +options+ supports
-
# three additional keys for convenience and conformance:
-
#
-
# * <tt>:alt</tt> - If no alt text is given, the file name part of the
-
# +source+ is used (capitalized and without the extension)
-
# * <tt>:size</tt> - Supplied as "{Width}x{Height}", so "30x45" becomes
-
# width="30" and height="45". <tt>:size</tt> will be ignored if the
-
# value is not in the correct format.
-
# * <tt>:mouseover</tt> - Set an alternate image to be used when the onmouseover
-
# event is fired, and sets the original image to be replaced onmouseout.
-
# This can be used to implement an easy image toggle that fires on onmouseover.
-
#
-
# ==== Examples
-
# image_tag("icon") # =>
-
# <img src="/images/icon" alt="Icon" />
-
# image_tag("icon.png") # =>
-
# <img src="/images/icon.png" alt="Icon" />
-
# image_tag("icon.png", :size => "16x10", :alt => "Edit Entry") # =>
-
# <img src="/images/icon.png" width="16" height="10" alt="Edit Entry" />
-
# image_tag("/icons/icon.gif", :size => "16x16") # =>
-
# <img src="/icons/icon.gif" width="16" height="16" alt="Icon" />
-
# image_tag("/icons/icon.gif", :height => '32', :width => '32') # =>
-
# <img alt="Icon" height="32" src="/icons/icon.gif" width="32" />
-
# image_tag("/icons/icon.gif", :class => "menu_icon") # =>
-
# <img alt="Icon" class="menu_icon" src="/icons/icon.gif" />
-
# image_tag("mouse.png", :mouseover => "/images/mouse_over.png") # =>
-
# <img src="/images/mouse.png" onmouseover="this.src='/images/mouse_over.png'" onmouseout="this.src='/images/mouse.png'" alt="Mouse" />
-
# image_tag("mouse.png", :mouseover => image_path("mouse_over.png")) # =>
-
# <img src="/images/mouse.png" onmouseover="this.src='/images/mouse_over.png'" onmouseout="this.src='/images/mouse.png'" alt="Mouse" />
-
1
def image_tag(source, options = {})
-
options.symbolize_keys!
-
-
src = options[:src] = path_to_image(source)
-
-
unless src =~ /^cid:/
-
options[:alt] = options.fetch(:alt){ image_alt(src) }
-
end
-
-
if size = options.delete(:size)
-
options[:width], options[:height] = size.split("x") if size =~ %r{^\d+x\d+$}
-
end
-
-
if mouseover = options.delete(:mouseover)
-
options[:onmouseover] = "this.src='#{path_to_image(mouseover)}'"
-
options[:onmouseout] = "this.src='#{src}'"
-
end
-
-
tag("img", options)
-
end
-
-
1
def image_alt(src)
-
File.basename(src, '.*').sub(/-[[:xdigit:]]{32}\z/, '').capitalize
-
end
-
-
# Returns an html video tag for the +sources+. If +sources+ is a string,
-
# a single video tag will be returned. If +sources+ is an array, a video
-
# tag with nested source tags for each source will be returned. The
-
# +sources+ can be full paths or files that exists in your public videos
-
# directory.
-
#
-
# ==== Options
-
# You can add HTML attributes using the +options+. The +options+ supports
-
# two additional keys for convenience and conformance:
-
#
-
# * <tt>:poster</tt> - Set an image (like a screenshot) to be shown
-
# before the video loads. The path is calculated like the +src+ of +image_tag+.
-
# * <tt>:size</tt> - Supplied as "{Width}x{Height}", so "30x45" becomes
-
# width="30" and height="45". <tt>:size</tt> will be ignored if the
-
# value is not in the correct format.
-
#
-
# ==== Examples
-
# video_tag("trailer") # =>
-
# <video src="/videos/trailer" />
-
# video_tag("trailer.ogg") # =>
-
# <video src="/videos/trailer.ogg" />
-
# video_tag("trailer.ogg", :controls => true, :autobuffer => true) # =>
-
# <video autobuffer="autobuffer" controls="controls" src="/videos/trailer.ogg" />
-
# video_tag("trailer.m4v", :size => "16x10", :poster => "screenshot.png") # =>
-
# <video src="/videos/trailer.m4v" width="16" height="10" poster="/images/screenshot.png" />
-
# video_tag("/trailers/hd.avi", :size => "16x16") # =>
-
# <video src="/trailers/hd.avi" width="16" height="16" />
-
# video_tag("/trailers/hd.avi", :height => '32', :width => '32') # =>
-
# <video height="32" src="/trailers/hd.avi" width="32" />
-
# video_tag(["trailer.ogg", "trailer.flv"]) # =>
-
# <video><source src="trailer.ogg" /><source src="trailer.ogg" /><source src="trailer.flv" /></video>
-
# video_tag(["trailer.ogg", "trailer.flv"] :size => "160x120") # =>
-
# <video height="120" width="160"><source src="trailer.ogg" /><source src="trailer.flv" /></video>
-
1
def video_tag(sources, options = {})
-
options.symbolize_keys!
-
-
options[:poster] = path_to_image(options[:poster]) if options[:poster]
-
-
if size = options.delete(:size)
-
options[:width], options[:height] = size.split("x") if size =~ %r{^\d+x\d+$}
-
end
-
-
if sources.is_a?(Array)
-
content_tag("video", options) do
-
sources.map { |source| tag("source", :src => source) }.join.html_safe
-
end
-
else
-
options[:src] = path_to_video(sources)
-
tag("video", options)
-
end
-
end
-
-
# Returns an html audio tag for the +source+.
-
# The +source+ can be full path or file that exists in
-
# your public audios directory.
-
#
-
# ==== Examples
-
# audio_tag("sound") # =>
-
# <audio src="/audios/sound" />
-
# audio_tag("sound.wav") # =>
-
# <audio src="/audios/sound.wav" />
-
# audio_tag("sound.wav", :autoplay => true, :controls => true) # =>
-
# <audio autoplay="autoplay" controls="controls" src="/audios/sound.wav" />
-
1
def audio_tag(source, options = {})
-
options.symbolize_keys!
-
options[:src] = path_to_audio(source)
-
tag("audio", options)
-
end
-
-
1
private
-
-
1
def asset_paths
-
@asset_paths ||= AssetTagHelper::AssetPaths.new(config, controller)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/string/inflections'
-
1
require 'active_support/core_ext/file'
-
1
require 'action_view/helpers/tag_helper'
-
-
1
module ActionView
-
1
module Helpers
-
1
module AssetTagHelper
-
-
1
class AssetIncludeTag
-
1
include TagHelper
-
-
1
attr_reader :config, :asset_paths
-
1
class_attribute :expansions
-
-
1
def self.inherited(base)
-
2
base.expansions = { }
-
end
-
-
1
def initialize(config, asset_paths)
-
@config = config
-
@asset_paths = asset_paths
-
end
-
-
1
def asset_name
-
raise NotImplementedError
-
end
-
-
1
def extension
-
raise NotImplementedError
-
end
-
-
1
def custom_dir
-
raise NotImplementedError
-
end
-
-
1
def asset_tag(source, options)
-
raise NotImplementedError
-
end
-
-
1
def include_tag(*sources)
-
options = sources.extract_options!.stringify_keys
-
concat = options.delete("concat")
-
cache = concat || options.delete("cache")
-
recursive = options.delete("recursive")
-
-
if concat || (config.perform_caching && cache)
-
joined_name = (cache == true ? "all" : cache) + ".#{extension}"
-
joined_path = File.join((joined_name[/^#{File::SEPARATOR}/] ? config.assets_dir : custom_dir), joined_name)
-
unless config.perform_caching && File.exists?(joined_path)
-
write_asset_file_contents(joined_path, compute_paths(sources, recursive))
-
end
-
asset_tag(joined_name, options)
-
else
-
sources = expand_sources(sources, recursive)
-
ensure_sources!(sources) if cache
-
sources.collect { |source| asset_tag(source, options) }.join("\n").html_safe
-
end
-
end
-
-
1
private
-
-
1
def path_to_asset(source, include_host = true, protocol = nil)
-
asset_paths.compute_public_path(source, asset_name.to_s.pluralize, extension, include_host, protocol)
-
end
-
-
1
def path_to_asset_source(source)
-
asset_paths.compute_source_path(source, asset_name.to_s.pluralize, extension)
-
end
-
-
1
def compute_paths(*args)
-
expand_sources(*args).collect { |source| path_to_asset_source(source) }
-
end
-
-
1
def expand_sources(sources, recursive)
-
if sources.first == :all
-
collect_asset_files(custom_dir, ('**' if recursive), "*.#{extension}")
-
else
-
sources.inject([]) do |list, source|
-
determined_source = determine_source(source, expansions)
-
update_source_list(list, determined_source)
-
end
-
end
-
end
-
-
1
def update_source_list(list, source)
-
case source
-
when String
-
list.delete(source)
-
list << source
-
when Array
-
updated_sources = source - list
-
list.concat(updated_sources)
-
end
-
end
-
-
1
def ensure_sources!(sources)
-
sources.each do |source|
-
asset_file_path!(path_to_asset_source(source))
-
end
-
end
-
-
1
def collect_asset_files(*path)
-
dir = path.first
-
-
Dir[File.join(*path.compact)].collect do |file|
-
file[-(file.size - dir.size - 1)..-1].sub(/\.\w+$/, '')
-
end.sort
-
end
-
-
1
def determine_source(source, collection)
-
case source
-
when Symbol
-
collection[source] || raise(ArgumentError, "No expansion found for #{source.inspect}")
-
else
-
source
-
end
-
end
-
-
1
def join_asset_file_contents(paths)
-
paths.collect { |path| File.read(asset_file_path!(path, true)) }.join("\n\n")
-
end
-
-
1
def write_asset_file_contents(joined_asset_path, asset_paths)
-
FileUtils.mkdir_p(File.dirname(joined_asset_path))
-
File.atomic_write(joined_asset_path) { |cache| cache.write(join_asset_file_contents(asset_paths)) }
-
-
# Set mtime to the latest of the combined files to allow for
-
# consistent ETag without a shared filesystem.
-
mt = asset_paths.map { |p| File.mtime(asset_file_path!(p)) }.max
-
File.utime(mt, mt, joined_asset_path)
-
end
-
-
1
def asset_file_path!(absolute_path, error_if_file_is_uri = false)
-
if asset_paths.is_uri?(absolute_path)
-
raise(Errno::ENOENT, "Asset file #{path} is uri and cannot be merged into single file") if error_if_file_is_uri
-
else
-
raise(Errno::ENOENT, "Asset file not found at '#{absolute_path}'" ) unless File.exist?(absolute_path)
-
return absolute_path
-
end
-
end
-
end
-
-
end
-
end
-
end
-
1
require 'thread'
-
1
require 'active_support/core_ext/file'
-
-
1
module ActionView
-
1
module Helpers
-
1
module AssetTagHelper
-
-
1
class AssetPaths < ::ActionView::AssetPaths #:nodoc:
-
# You can enable or disable the asset tag ids cache.
-
# With the cache enabled, the asset tag helper methods will make fewer
-
# expensive file system calls (the default implementation checks the file
-
# system timestamp). However this prevents you from modifying any asset
-
# files while the server is running.
-
#
-
# ActionView::Helpers::AssetTagHelper::AssetPaths.cache_asset_ids = false
-
1
mattr_accessor :cache_asset_ids
-
-
# Add or change an asset id in the asset id cache. This can be used
-
# for SASS on Heroku.
-
# :api: public
-
1
def add_to_asset_ids_cache(source, asset_id)
-
self.asset_ids_cache_guard.synchronize do
-
self.asset_ids_cache[source] = asset_id
-
end
-
end
-
-
1
private
-
-
1
def rewrite_extension(source, dir, ext)
-
source_ext = File.extname(source)
-
-
source_with_ext = if source_ext.empty?
-
"#{source}.#{ext}"
-
elsif ext != source_ext[1..-1]
-
with_ext = "#{source}.#{ext}"
-
with_ext if File.exist?(File.join(config.assets_dir, dir, with_ext))
-
end
-
-
source_with_ext || source
-
end
-
-
# Break out the asset path rewrite in case plugins wish to put the asset id
-
# someplace other than the query string.
-
1
def rewrite_asset_path(source, dir)
-
source = "/#{dir}/#{source}" unless source[0] == ?/
-
path = config.asset_path
-
-
if path && path.respond_to?(:call)
-
return path.call(source)
-
elsif path && path.is_a?(String)
-
return path % [source]
-
end
-
-
asset_id = rails_asset_id(source)
-
if asset_id.empty?
-
source
-
else
-
"#{source}?#{asset_id}"
-
end
-
end
-
-
1
mattr_accessor :asset_ids_cache
-
1
self.asset_ids_cache = {}
-
-
1
mattr_accessor :asset_ids_cache_guard
-
1
self.asset_ids_cache_guard = Mutex.new
-
-
# Use the RAILS_ASSET_ID environment variable or the source's
-
# modification time as its cache-busting asset id.
-
1
def rails_asset_id(source)
-
if asset_id = ENV["RAILS_ASSET_ID"]
-
asset_id
-
else
-
if self.cache_asset_ids && (asset_id = self.asset_ids_cache[source])
-
asset_id
-
else
-
path = File.join(config.assets_dir, source)
-
asset_id = File.exist?(path) ? File.mtime(path).to_i.to_s : ''
-
-
if self.cache_asset_ids
-
add_to_asset_ids_cache(source, asset_id)
-
end
-
-
asset_id
-
end
-
end
-
end
-
end
-
-
end
-
end
-
end
-
1
require 'active_support/concern'
-
1
require 'active_support/core_ext/file'
-
1
require 'action_view/helpers/asset_tag_helpers/asset_include_tag'
-
-
1
module ActionView
-
1
module Helpers
-
1
module AssetTagHelper
-
-
1
class JavascriptIncludeTag < AssetIncludeTag
-
1
def asset_name
-
'javascript'
-
end
-
-
1
def extension
-
'js'
-
end
-
-
1
def asset_tag(source, options)
-
content_tag("script", "", { "type" => Mime::JS, "src" => path_to_asset(source) }.merge(options))
-
end
-
-
1
def custom_dir
-
config.javascripts_dir
-
end
-
-
1
private
-
-
1
def expand_sources(sources, recursive = false)
-
if sources.include?(:all)
-
all_asset_files = (collect_asset_files(custom_dir, ('**' if recursive), "*.#{extension}") - ['application']) << 'application'
-
((determine_source(:defaults, expansions).dup & all_asset_files) + all_asset_files).uniq
-
else
-
expanded_sources = sources.inject([]) do |list, source|
-
determined_source = determine_source(source, expansions)
-
update_source_list(list, determined_source)
-
end
-
add_application_js(expanded_sources, sources)
-
expanded_sources
-
end
-
end
-
-
1
def add_application_js(expanded_sources, sources)
-
if sources.include?(:defaults) && File.exist?(File.join(custom_dir, "application.#{extension}"))
-
expanded_sources.delete('application')
-
expanded_sources << "application"
-
end
-
end
-
end
-
-
-
1
module JavascriptTagHelpers
-
1
extend ActiveSupport::Concern
-
-
1
module ClassMethods
-
# Register one or more javascript files to be included when <tt>symbol</tt>
-
# is passed to <tt>javascript_include_tag</tt>. This method is typically intended
-
# to be called from plugin initialization to register javascript files
-
# that the plugin installed in <tt>public/javascripts</tt>.
-
#
-
# ActionView::Helpers::AssetTagHelper.register_javascript_expansion :monkey => ["head", "body", "tail"]
-
#
-
# javascript_include_tag :monkey # =>
-
# <script type="text/javascript" src="/javascripts/head.js"></script>
-
# <script type="text/javascript" src="/javascripts/body.js"></script>
-
# <script type="text/javascript" src="/javascripts/tail.js"></script>
-
1
def register_javascript_expansion(expansions)
-
1
js_expansions = JavascriptIncludeTag.expansions
-
1
expansions.each do |key, values|
-
1
js_expansions[key] = (js_expansions[key] || []) | Array(values)
-
end
-
end
-
end
-
-
# Computes the path to a javascript asset in the public javascripts directory.
-
# If the +source+ filename has no extension, .js will be appended (except for explicit URIs)
-
# Full paths from the document root will be passed through.
-
# Used internally by javascript_include_tag to build the script path.
-
#
-
# ==== Examples
-
# javascript_path "xmlhr" # => /javascripts/xmlhr.js
-
# javascript_path "dir/xmlhr.js" # => /javascripts/dir/xmlhr.js
-
# javascript_path "/dir/xmlhr" # => /dir/xmlhr.js
-
# javascript_path "http://www.example.com/js/xmlhr" # => http://www.example.com/js/xmlhr
-
# javascript_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js
-
1
def javascript_path(source)
-
asset_paths.compute_public_path(source, 'javascripts', 'js')
-
end
-
1
alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with a javascript_path named route
-
-
# Returns an HTML script tag for each of the +sources+ provided.
-
#
-
# Sources may be paths to JavaScript files. Relative paths are assumed to be relative
-
# to <tt>public/javascripts</tt>, full paths are assumed to be relative to the document
-
# root. Relative paths are idiomatic, use absolute paths only when needed.
-
#
-
# When passing paths, the ".js" extension is optional.
-
#
-
# If the application is not using the asset pipeline, to include the default JavaScript
-
# expansion pass <tt>:defaults</tt> as source. By default, <tt>:defaults</tt> loads jQuery,
-
# and that can be overridden in <tt>config/application.rb</tt>:
-
#
-
# config.action_view.javascript_expansions[:defaults] = %w(foo.js bar.js)
-
#
-
# When using <tt>:defaults</tt>, if an <tt>application.js</tt> file exists in
-
# <tt>public/javascripts</tt> it will be included as well at the end.
-
#
-
# You can modify the HTML attributes of the script tag by passing a hash as the
-
# last argument.
-
#
-
# ==== Examples
-
# javascript_include_tag "xmlhr"
-
# # => <script type="text/javascript" src="/javascripts/xmlhr.js?1284139606"></script>
-
#
-
# javascript_include_tag "xmlhr.js"
-
# # => <script type="text/javascript" src="/javascripts/xmlhr.js?1284139606"></script>
-
#
-
# javascript_include_tag "common.javascript", "/elsewhere/cools"
-
# # => <script type="text/javascript" src="/javascripts/common.javascript?1284139606"></script>
-
# # <script type="text/javascript" src="/elsewhere/cools.js?1423139606"></script>
-
#
-
# javascript_include_tag "http://www.example.com/xmlhr"
-
# # => <script type="text/javascript" src="http://www.example.com/xmlhr.js?1284139606"></script>
-
#
-
# javascript_include_tag "http://www.example.com/xmlhr.js"
-
# # => <script type="text/javascript" src="http://www.example.com/xmlhr.js?1284139606"></script>
-
#
-
# javascript_include_tag :defaults
-
# # => <script type="text/javascript" src="/javascripts/jquery.js?1284139606"></script>
-
# # <script type="text/javascript" src="/javascripts/rails.js?1284139606"></script>
-
# # <script type="text/javascript" src="/javascripts/application.js?1284139606"></script>
-
#
-
# * = The application.js file is only referenced if it exists
-
#
-
# You can also include all JavaScripts in the +javascripts+ directory using <tt>:all</tt> as the source:
-
#
-
# javascript_include_tag :all
-
# # => <script type="text/javascript" src="/javascripts/jquery.js?1284139606"></script>
-
# # <script type="text/javascript" src="/javascripts/rails.js?1284139606"></script>
-
# # <script type="text/javascript" src="/javascripts/application.js?1284139606"></script>
-
# # <script type="text/javascript" src="/javascripts/shop.js?1284139606"></script>
-
# # <script type="text/javascript" src="/javascripts/checkout.js?1284139606"></script>
-
#
-
# Note that your defaults of choice will be included first, so they will be available to all subsequently
-
# included files.
-
#
-
# If you want Rails to search in all the subdirectories under <tt>public/javascripts</tt>, you should
-
# explicitly set <tt>:recursive</tt>:
-
#
-
# javascript_include_tag :all, :recursive => true
-
#
-
# == Caching multiple JavaScripts into one
-
#
-
# You can also cache multiple JavaScripts into one file, which requires less HTTP connections to download
-
# and can better be compressed by gzip (leading to faster transfers). Caching will only happen if
-
# <tt>config.perform_caching</tt> is set to true (which is the case by default for the Rails
-
# production environment, but not for the development environment).
-
#
-
# ==== Examples
-
#
-
# # assuming config.perform_caching is false
-
# javascript_include_tag :all, :cache => true
-
# # => <script type="text/javascript" src="/javascripts/jquery.js?1284139606"></script>
-
# # <script type="text/javascript" src="/javascripts/rails.js?1284139606"></script>
-
# # <script type="text/javascript" src="/javascripts/application.js?1284139606"></script>
-
# # <script type="text/javascript" src="/javascripts/shop.js?1284139606"></script>
-
# # <script type="text/javascript" src="/javascripts/checkout.js?1284139606"></script>
-
#
-
# # assuming config.perform_caching is true
-
# javascript_include_tag :all, :cache => true
-
# # => <script type="text/javascript" src="/javascripts/all.js?1344139789"></script>
-
#
-
# # assuming config.perform_caching is false
-
# javascript_include_tag "jquery", "cart", "checkout", :cache => "shop"
-
# # => <script type="text/javascript" src="/javascripts/jquery.js?1284139606"></script>
-
# # <script type="text/javascript" src="/javascripts/cart.js?1289139157"></script>
-
# # <script type="text/javascript" src="/javascripts/checkout.js?1299139816"></script>
-
#
-
# # assuming config.perform_caching is true
-
# javascript_include_tag "jquery", "cart", "checkout", :cache => "shop"
-
# # => <script type="text/javascript" src="/javascripts/shop.js?1299139816"></script>
-
#
-
# The <tt>:recursive</tt> option is also available for caching:
-
#
-
# javascript_include_tag :all, :cache => true, :recursive => true
-
1
def javascript_include_tag(*sources)
-
@javascript_include ||= JavascriptIncludeTag.new(config, asset_paths)
-
@javascript_include.include_tag(*sources)
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/concern'
-
1
require 'active_support/core_ext/file'
-
1
require 'action_view/helpers/asset_tag_helpers/asset_include_tag'
-
-
1
module ActionView
-
1
module Helpers
-
1
module AssetTagHelper
-
-
1
class StylesheetIncludeTag < AssetIncludeTag
-
1
def asset_name
-
'stylesheet'
-
end
-
-
1
def extension
-
'css'
-
end
-
-
1
def asset_tag(source, options)
-
# We force the :request protocol here to avoid a double-download bug in IE7 and IE8
-
tag("link", { "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen", "href" => ERB::Util.html_escape(path_to_asset(source, true, :request)) }.merge(options), false, false)
-
end
-
-
1
def custom_dir
-
config.stylesheets_dir
-
end
-
end
-
-
-
1
module StylesheetTagHelpers
-
1
extend ActiveSupport::Concern
-
-
1
module ClassMethods
-
# Register one or more stylesheet files to be included when <tt>symbol</tt>
-
# is passed to <tt>stylesheet_link_tag</tt>. This method is typically intended
-
# to be called from plugin initialization to register stylesheet files
-
# that the plugin installed in <tt>public/stylesheets</tt>.
-
#
-
# ActionView::Helpers::AssetTagHelper.register_stylesheet_expansion :monkey => ["head", "body", "tail"]
-
#
-
# stylesheet_link_tag :monkey # =>
-
# <link href="/stylesheets/head.css" media="screen" rel="stylesheet" type="text/css" />
-
# <link href="/stylesheets/body.css" media="screen" rel="stylesheet" type="text/css" />
-
# <link href="/stylesheets/tail.css" media="screen" rel="stylesheet" type="text/css" />
-
1
def register_stylesheet_expansion(expansions)
-
1
style_expansions = StylesheetIncludeTag.expansions
-
1
expansions.each do |key, values|
-
style_expansions[key] = (style_expansions[key] || []) | Array(values)
-
end
-
end
-
end
-
-
# Computes the path to a stylesheet asset in the public stylesheets directory.
-
# If the +source+ filename has no extension, <tt>.css</tt> will be appended (except for explicit URIs).
-
# Full paths from the document root will be passed through.
-
# Used internally by +stylesheet_link_tag+ to build the stylesheet path.
-
#
-
# ==== Examples
-
# stylesheet_path "style" # => /stylesheets/style.css
-
# stylesheet_path "dir/style.css" # => /stylesheets/dir/style.css
-
# stylesheet_path "/dir/style.css" # => /dir/style.css
-
# stylesheet_path "http://www.example.com/css/style" # => http://www.example.com/css/style
-
# stylesheet_path "http://www.example.com/css/style.css" # => http://www.example.com/css/style.css
-
1
def stylesheet_path(source)
-
asset_paths.compute_public_path(source, 'stylesheets', 'css', true, :request)
-
end
-
1
alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route
-
-
# Returns a stylesheet link tag for the sources specified as arguments. If
-
# you don't specify an extension, <tt>.css</tt> will be appended automatically.
-
# You can modify the link attributes by passing a hash as the last argument.
-
#
-
# ==== Examples
-
# stylesheet_link_tag "style" # =>
-
# <link href="/stylesheets/style.css" media="screen" rel="stylesheet" type="text/css" />
-
#
-
# stylesheet_link_tag "style.css" # =>
-
# <link href="/stylesheets/style.css" media="screen" rel="stylesheet" type="text/css" />
-
#
-
# stylesheet_link_tag "http://www.example.com/style.css" # =>
-
# <link href="http://www.example.com/style.css" media="screen" rel="stylesheet" type="text/css" />
-
#
-
# stylesheet_link_tag "style", :media => "all" # =>
-
# <link href="/stylesheets/style.css" media="all" rel="stylesheet" type="text/css" />
-
#
-
# stylesheet_link_tag "style", :media => "print" # =>
-
# <link href="/stylesheets/style.css" media="print" rel="stylesheet" type="text/css" />
-
#
-
# stylesheet_link_tag "random.styles", "/css/stylish" # =>
-
# <link href="/stylesheets/random.styles" media="screen" rel="stylesheet" type="text/css" />
-
# <link href="/css/stylish.css" media="screen" rel="stylesheet" type="text/css" />
-
#
-
# You can also include all styles in the stylesheets directory using <tt>:all</tt> as the source:
-
#
-
# stylesheet_link_tag :all # =>
-
# <link href="/stylesheets/style1.css" media="screen" rel="stylesheet" type="text/css" />
-
# <link href="/stylesheets/styleB.css" media="screen" rel="stylesheet" type="text/css" />
-
# <link href="/stylesheets/styleX2.css" media="screen" rel="stylesheet" type="text/css" />
-
#
-
# If you want Rails to search in all the subdirectories under stylesheets, you should explicitly set <tt>:recursive</tt>:
-
#
-
# stylesheet_link_tag :all, :recursive => true
-
#
-
# == Caching multiple stylesheets into one
-
#
-
# You can also cache multiple stylesheets into one file, which requires less HTTP connections and can better be
-
# compressed by gzip (leading to faster transfers). Caching will only happen if config.perform_caching
-
# is set to true (which is the case by default for the Rails production environment, but not for the development
-
# environment). Examples:
-
#
-
# ==== Examples
-
# stylesheet_link_tag :all, :cache => true # when config.perform_caching is false =>
-
# <link href="/stylesheets/style1.css" media="screen" rel="stylesheet" type="text/css" />
-
# <link href="/stylesheets/styleB.css" media="screen" rel="stylesheet" type="text/css" />
-
# <link href="/stylesheets/styleX2.css" media="screen" rel="stylesheet" type="text/css" />
-
#
-
# stylesheet_link_tag :all, :cache => true # when config.perform_caching is true =>
-
# <link href="/stylesheets/all.css" media="screen" rel="stylesheet" type="text/css" />
-
#
-
# stylesheet_link_tag "shop", "cart", "checkout", :cache => "payment" # when config.perform_caching is false =>
-
# <link href="/stylesheets/shop.css" media="screen" rel="stylesheet" type="text/css" />
-
# <link href="/stylesheets/cart.css" media="screen" rel="stylesheet" type="text/css" />
-
# <link href="/stylesheets/checkout.css" media="screen" rel="stylesheet" type="text/css" />
-
#
-
# stylesheet_link_tag "shop", "cart", "checkout", :cache => "payment" # when config.perform_caching is true =>
-
# <link href="/stylesheets/payment.css" media="screen" rel="stylesheet" type="text/css" />
-
#
-
# The <tt>:recursive</tt> option is also available for caching:
-
#
-
# stylesheet_link_tag :all, :cache => true, :recursive => true
-
#
-
# To force concatenation (even in development mode) set <tt>:concat</tt> to true. This is useful if
-
# you have too many stylesheets for IE to load.
-
#
-
# stylesheet_link_tag :all, :concat => true
-
#
-
1
def stylesheet_link_tag(*sources)
-
@stylesheet_include ||= StylesheetIncludeTag.new(config, asset_paths)
-
@stylesheet_include.include_tag(*sources)
-
end
-
-
end
-
-
end
-
end
-
end
-
1
require 'set'
-
-
1
module ActionView
-
# = Action View Atom Feed Helpers
-
1
module Helpers #:nodoc:
-
1
module AtomFeedHelper
-
# Adds easy defaults to writing Atom feeds with the Builder template engine (this does not work on ERB or any other
-
# template languages).
-
#
-
# Full usage example:
-
#
-
# config/routes.rb:
-
# Basecamp::Application.routes.draw do
-
# resources :posts
-
# root :to => "posts#index"
-
# end
-
#
-
# app/controllers/posts_controller.rb:
-
# class PostsController < ApplicationController::Base
-
# # GET /posts.html
-
# # GET /posts.atom
-
# def index
-
# @posts = Post.find(:all)
-
#
-
# respond_to do |format|
-
# format.html
-
# format.atom
-
# end
-
# end
-
# end
-
#
-
# app/views/posts/index.atom.builder:
-
# atom_feed do |feed|
-
# feed.title("My great blog!")
-
# feed.updated(@posts.first.created_at)
-
#
-
# @posts.each do |post|
-
# feed.entry(post) do |entry|
-
# entry.title(post.title)
-
# entry.content(post.body, :type => 'html')
-
#
-
# entry.author do |author|
-
# author.name("DHH")
-
# end
-
# end
-
# end
-
# end
-
#
-
# The options for atom_feed are:
-
#
-
# * <tt>:language</tt>: Defaults to "en-US".
-
# * <tt>:root_url</tt>: The HTML alternative that this feed is doubling for. Defaults to / on the current host.
-
# * <tt>:url</tt>: The URL for this feed. Defaults to the current URL.
-
# * <tt>:id</tt>: The id for this feed. Defaults to "tag:#{request.host},#{options[:schema_date]}:#{request.fullpath.split(".")[0]}"
-
# * <tt>:schema_date</tt>: The date at which the tag scheme for the feed was first used. A good default is the year you
-
# created the feed. See http://feedvalidator.org/docs/error/InvalidTAG.html for more information. If not specified,
-
# 2005 is used (as an "I don't care" value).
-
# * <tt>:instruct</tt>: Hash of XML processing instructions in the form {target => {attribute => value, }} or {target => [{attribute => value, }, ]}
-
#
-
# Other namespaces can be added to the root element:
-
#
-
# app/views/posts/index.atom.builder:
-
# atom_feed({'xmlns:app' => 'http://www.w3.org/2007/app',
-
# 'xmlns:openSearch' => 'http://a9.com/-/spec/opensearch/1.1/'}) do |feed|
-
# feed.title("My great blog!")
-
# feed.updated((@posts.first.created_at))
-
# feed.tag!(openSearch:totalResults, 10)
-
#
-
# @posts.each do |post|
-
# feed.entry(post) do |entry|
-
# entry.title(post.title)
-
# entry.content(post.body, :type => 'html')
-
# entry.tag!('app:edited', Time.now)
-
#
-
# entry.author do |author|
-
# author.name("DHH")
-
# end
-
# end
-
# end
-
# end
-
#
-
# The Atom spec defines five elements (content rights title subtitle
-
# summary) which may directly contain xhtml content if :type => 'xhtml'
-
# is specified as an attribute. If so, this helper will take care of
-
# the enclosing div and xhtml namespace declaration. Example usage:
-
#
-
# entry.summary :type => 'xhtml' do |xhtml|
-
# xhtml.p pluralize(order.line_items.count, "line item")
-
# xhtml.p "Shipped to #{order.address}"
-
# xhtml.p "Paid by #{order.pay_type}"
-
# end
-
#
-
#
-
# <tt>atom_feed</tt> yields an +AtomFeedBuilder+ instance. Nested elements yield
-
# an +AtomBuilder+ instance.
-
1
def atom_feed(options = {}, &block)
-
if options[:schema_date]
-
options[:schema_date] = options[:schema_date].strftime("%Y-%m-%d") if options[:schema_date].respond_to?(:strftime)
-
else
-
options[:schema_date] = "2005" # The Atom spec copyright date
-
end
-
-
xml = options.delete(:xml) || eval("xml", block.binding)
-
xml.instruct!
-
if options[:instruct]
-
options[:instruct].each do |target,attrs|
-
if attrs.respond_to?(:keys)
-
xml.instruct!(target, attrs)
-
elsif attrs.respond_to?(:each)
-
attrs.each { |attr_group| xml.instruct!(target, attr_group) }
-
end
-
end
-
end
-
-
feed_opts = {"xml:lang" => options[:language] || "en-US", "xmlns" => 'http://www.w3.org/2005/Atom'}
-
feed_opts.merge!(options).reject!{|k,v| !k.to_s.match(/^xml/)}
-
-
xml.feed(feed_opts) do
-
xml.id(options[:id] || "tag:#{request.host},#{options[:schema_date]}:#{request.fullpath.split(".")[0]}")
-
xml.link(:rel => 'alternate', :type => 'text/html', :href => options[:root_url] || (request.protocol + request.host_with_port))
-
xml.link(:rel => 'self', :type => 'application/atom+xml', :href => options[:url] || request.url)
-
-
yield AtomFeedBuilder.new(xml, self, options)
-
end
-
end
-
-
1
class AtomBuilder
-
1
XHTML_TAG_NAMES = %w(content rights title subtitle summary).to_set
-
-
1
def initialize(xml)
-
@xml = xml
-
end
-
-
1
private
-
# Delegate to xml builder, first wrapping the element in a xhtml
-
# namespaced div element if the method and arguments indicate
-
# that an xhtml_block? is desired.
-
1
def method_missing(method, *arguments, &block)
-
if xhtml_block?(method, arguments)
-
@xml.__send__(method, *arguments) do
-
@xml.div(:xmlns => 'http://www.w3.org/1999/xhtml') do |xhtml|
-
block.call(xhtml)
-
end
-
end
-
else
-
@xml.__send__(method, *arguments, &block)
-
end
-
end
-
-
# True if the method name matches one of the five elements defined
-
# in the Atom spec as potentially containing XHTML content and
-
# if :type => 'xhtml' is, in fact, specified.
-
1
def xhtml_block?(method, arguments)
-
if XHTML_TAG_NAMES.include?(method.to_s)
-
last = arguments.last
-
last.is_a?(Hash) && last[:type].to_s == 'xhtml'
-
end
-
end
-
end
-
-
1
class AtomFeedBuilder < AtomBuilder
-
1
def initialize(xml, view, feed_options = {})
-
@xml, @view, @feed_options = xml, view, feed_options
-
end
-
-
# Accepts a Date or Time object and inserts it in the proper format. If nil is passed, current time in UTC is used.
-
1
def updated(date_or_time = nil)
-
@xml.updated((date_or_time || Time.now.utc).xmlschema)
-
end
-
-
# Creates an entry tag for a specific record and prefills the id using class and id.
-
#
-
# Options:
-
#
-
# * <tt>:published</tt>: Time first published. Defaults to the created_at attribute on the record if one such exists.
-
# * <tt>:updated</tt>: Time of update. Defaults to the updated_at attribute on the record if one such exists.
-
# * <tt>:url</tt>: The URL for this entry. Defaults to the polymorphic_url for the record.
-
# * <tt>:id</tt>: The ID for this entry. Defaults to "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}"
-
1
def entry(record, options = {})
-
@xml.entry do
-
@xml.id(options[:id] || "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}")
-
-
if options[:published] || (record.respond_to?(:created_at) && record.created_at)
-
@xml.published((options[:published] || record.created_at).xmlschema)
-
end
-
-
if options[:updated] || (record.respond_to?(:updated_at) && record.updated_at)
-
@xml.updated((options[:updated] || record.updated_at).xmlschema)
-
end
-
-
@xml.link(:rel => 'alternate', :type => 'text/html', :href => options[:url] || @view.polymorphic_url(record))
-
-
yield AtomBuilder.new(@xml)
-
end
-
end
-
end
-
-
end
-
end
-
end
-
1
module ActionView
-
# = Action View Cache Helper
-
1
module Helpers
-
1
module CacheHelper
-
# This helper exposes a method for caching fragments of a view
-
# rather than an entire action or page. This technique is useful
-
# caching pieces like menus, lists of newstopics, static HTML
-
# fragments, and so on. This method takes a block that contains
-
# the content you wish to cache.
-
#
-
# See ActionController::Caching::Fragments for usage instructions.
-
#
-
# ==== Examples
-
# If you want to cache a navigation menu, you can do following:
-
#
-
# <% cache do %>
-
# <%= render :partial => "menu" %>
-
# <% end %>
-
#
-
# You can also cache static content:
-
#
-
# <% cache do %>
-
# <p>Hello users! Welcome to our website!</p>
-
# <% end %>
-
#
-
# Static content with embedded ruby content can be cached as
-
# well:
-
#
-
# <% cache do %>
-
# Topics:
-
# <%= render :partial => "topics", :collection => @topic_list %>
-
# <i>Topics listed alphabetically</i>
-
# <% end %>
-
1
def cache(name = {}, options = nil, &block)
-
if controller.perform_caching
-
safe_concat(fragment_for(name, options, &block))
-
else
-
yield
-
end
-
-
nil
-
end
-
-
1
private
-
# TODO: Create an object that has caching read/write on it
-
1
def fragment_for(name = {}, options = nil, &block) #:nodoc:
-
if fragment = controller.read_fragment(name, options)
-
fragment
-
else
-
# VIEW TODO: Make #capture usable outside of ERB
-
# This dance is needed because Builder can't use capture
-
pos = output_buffer.length
-
yield
-
output_safe = output_buffer.html_safe?
-
fragment = output_buffer.slice!(pos..-1)
-
if output_safe
-
self.output_buffer = output_buffer.class.new(output_buffer)
-
end
-
controller.write_fragment(name, fragment, options)
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/string/output_safety'
-
-
1
module ActionView
-
# = Action View Capture Helper
-
1
module Helpers
-
# CaptureHelper exposes methods to let you extract generated markup which
-
# can be used in other parts of a template or layout file.
-
#
-
# It provides a method to capture blocks into variables through capture and
-
# a way to capture a block of markup for use in a layout through content_for.
-
1
module CaptureHelper
-
# The capture method allows you to extract part of a template into a
-
# variable. You can then use this variable anywhere in your templates or layout.
-
#
-
# ==== Examples
-
# The capture method can be used in ERB templates...
-
#
-
# <% @greeting = capture do %>
-
# Welcome to my shiny new web page! The date and time is
-
# <%= Time.now %>
-
# <% end %>
-
#
-
# ...and Builder (RXML) templates.
-
#
-
# @timestamp = capture do
-
# "The current timestamp is #{Time.now}."
-
# end
-
#
-
# You can then use that variable anywhere else. For example:
-
#
-
# <html>
-
# <head><title><%= @greeting %></title></head>
-
# <body>
-
# <b><%= @greeting %></b>
-
# </body></html>
-
#
-
1
def capture(*args)
-
value = nil
-
buffer = with_output_buffer { value = yield(*args) }
-
if string = buffer.presence || value and string.is_a?(String)
-
ERB::Util.html_escape string
-
end
-
end
-
-
# Calling content_for stores a block of markup in an identifier for later use.
-
# You can make subsequent calls to the stored content in other templates, helper modules
-
# or the layout by passing the identifier as an argument to <tt>content_for</tt>.
-
#
-
# Note: <tt>yield</tt> can still be used to retrieve the stored content, but calling
-
# <tt>yield</tt> doesn't work in helper modules, while <tt>content_for</tt> does.
-
#
-
# ==== Examples
-
#
-
# <% content_for :not_authorized do %>
-
# alert('You are not authorized to do that!')
-
# <% end %>
-
#
-
# You can then use <tt>content_for :not_authorized</tt> anywhere in your templates.
-
#
-
# <%= content_for :not_authorized if current_user.nil? %>
-
#
-
# This is equivalent to:
-
#
-
# <%= yield :not_authorized if current_user.nil? %>
-
#
-
# <tt>content_for</tt>, however, can also be used in helper modules.
-
#
-
# module StorageHelper
-
# def stored_content
-
# content_for(:storage) || "Your storage is empty"
-
# end
-
# end
-
#
-
# This helper works just like normal helpers.
-
#
-
# <%= stored_content %>
-
#
-
# You can use the <tt>yield</tt> syntax alongside an existing call to <tt>yield</tt> in a layout. For example:
-
#
-
# <%# This is the layout %>
-
# <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-
# <head>
-
# <title>My Website</title>
-
# <%= yield :script %>
-
# </head>
-
# <body>
-
# <%= yield %>
-
# </body>
-
# </html>
-
#
-
# And now, we'll create a view that has a <tt>content_for</tt> call that
-
# creates the <tt>script</tt> identifier.
-
#
-
# <%# This is our view %>
-
# Please login!
-
#
-
# <% content_for :script do %>
-
# <script type="text/javascript">alert('You are not authorized to view this page!')</script>
-
# <% end %>
-
#
-
# Then, in another view, you could to do something like this:
-
#
-
# <%= link_to 'Logout', :action => 'logout', :remote => true %>
-
#
-
# <% content_for :script do %>
-
# <%= javascript_include_tag :defaults %>
-
# <% end %>
-
#
-
# That will place +script+ tags for your default set of JavaScript files on the page;
-
# this technique is useful if you'll only be using these scripts in a few views.
-
#
-
# Note that content_for concatenates the blocks it is given for a particular
-
# identifier in order. For example:
-
#
-
# <% content_for :navigation do %>
-
# <li><%= link_to 'Home', :action => 'index' %></li>
-
# <% end %>
-
#
-
# <%# Add some other content, or use a different template: %>
-
#
-
# <% content_for :navigation do %>
-
# <li><%= link_to 'Login', :action => 'login' %></li>
-
# <% end %>
-
#
-
# Then, in another template or layout, this code would render both links in order:
-
#
-
# <ul><%= content_for :navigation %></ul>
-
#
-
# Lastly, simple content can be passed as a parameter:
-
#
-
# <% content_for :script, javascript_include_tag(:defaults) %>
-
#
-
# WARNING: content_for is ignored in caches. So you shouldn't use it
-
# for elements that will be fragment cached.
-
1
def content_for(name, content = nil, &block)
-
content = capture(&block) if block_given?
-
if content
-
@view_flow.append(name, content)
-
nil
-
else
-
@view_flow.get(name)
-
end
-
end
-
-
# The same as +content_for+ but when used with streaming flushes
-
# straight back to the layout. In other words, if you want to
-
# concatenate several times to the same buffer when rendering a given
-
# template, you should use +content_for+, if not, use +provide+ to tell
-
# the layout to stop looking for more contents.
-
1
def provide(name, content = nil, &block)
-
content = capture(&block) if block_given?
-
result = @view_flow.append!(name, content) if content
-
result unless content
-
end
-
-
# content_for? simply checks whether any content has been captured yet using content_for
-
# Useful to render parts of your layout differently based on what is in your views.
-
#
-
# ==== Examples
-
#
-
# Perhaps you will use different css in you layout if no content_for :right_column
-
#
-
# <%# This is the layout %>
-
# <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-
# <head>
-
# <title>My Website</title>
-
# <%= yield :script %>
-
# </head>
-
# <body class="<%= content_for?(:right_col) ? 'one-column' : 'two-column' %>">
-
# <%= yield %>
-
# <%= yield :right_col %>
-
# </body>
-
# </html>
-
1
def content_for?(name)
-
@view_flow.get(name).present?
-
end
-
-
# Use an alternate output buffer for the duration of the block.
-
# Defaults to a new empty string.
-
1
def with_output_buffer(buf = nil) #:nodoc:
-
unless buf
-
buf = ActionView::OutputBuffer.new
-
buf.force_encoding(output_buffer.encoding) if output_buffer.respond_to?(:encoding) && buf.respond_to?(:force_encoding)
-
end
-
self.output_buffer, old_buffer = buf, output_buffer
-
yield
-
output_buffer
-
ensure
-
self.output_buffer = old_buffer
-
end
-
-
# Add the output buffer to the response body and start a new one.
-
1
def flush_output_buffer #:nodoc:
-
if output_buffer && !output_buffer.empty?
-
response.body_parts << output_buffer
-
self.output_buffer = output_buffer[0,0]
-
nil
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/attr_internal'
-
-
1
module ActionView
-
1
module Helpers
-
# This module keeps all methods and behavior in ActionView
-
# that simply delegates to the controller.
-
1
module ControllerHelper #:nodoc:
-
1
attr_internal :controller, :request
-
-
1
delegate :request_forgery_protection_token, :params, :session, :cookies, :response, :headers,
-
:flash, :action_name, :controller_name, :controller_path, :to => :controller
-
-
1
delegate :logger, :to => :controller, :allow_nil => true
-
-
1
def assign_controller(controller)
-
if @_controller = controller
-
@_request = controller.request if controller.respond_to?(:request)
-
@_config = controller.config.inheritable_copy if controller.respond_to?(:config)
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/string/strip'
-
-
1
module ActionView
-
# = Action View CSRF Helper
-
1
module Helpers
-
1
module CsrfHelper
-
# Returns meta tags "csrf-param" and "csrf-token" with the name of the cross-site
-
# request forgery protection parameter and token, respectively.
-
#
-
# <head>
-
# <%= csrf_meta_tags %>
-
# </head>
-
#
-
# These are used to generate the dynamic forms that implement non-remote links with
-
# <tt>:method</tt>.
-
#
-
# Note that regular forms generate hidden fields, and that Ajax calls are whitelisted,
-
# so they do not use these tags.
-
1
def csrf_meta_tags
-
if protect_against_forgery?
-
[
-
tag('meta', :name => 'csrf-param', :content => request_forgery_protection_token),
-
tag('meta', :name => 'csrf-token', :content => form_authenticity_token)
-
].join("\n").html_safe
-
end
-
end
-
-
# For backwards compatibility.
-
1
alias csrf_meta_tag csrf_meta_tags
-
end
-
end
-
end
-
1
require 'date'
-
1
require 'action_view/helpers/tag_helper'
-
1
require 'active_support/core_ext/date/conversions'
-
1
require 'active_support/core_ext/hash/slice'
-
1
require 'active_support/core_ext/object/with_options'
-
-
1
module ActionView
-
1
module Helpers
-
# = Action View Date Helpers
-
#
-
# The Date Helper primarily creates select/option tags for different kinds of dates and times or date and time
-
# elements. All of the select-type methods share a number of common options that are as follows:
-
#
-
# * <tt>:prefix</tt> - overwrites the default prefix of "date" used for the select names. So specifying "birthday"
-
# would give birthday[month] instead of date[month] if passed to the <tt>select_month</tt> method.
-
# * <tt>:include_blank</tt> - set to true if it should be possible to set an empty date.
-
# * <tt>:discard_type</tt> - set to true if you want to discard the type part of the select name. If set to true,
-
# the <tt>select_month</tt> method would use simply "date" (which can be overwritten using <tt>:prefix</tt>) instead
-
# of "date[month]".
-
1
module DateHelper
-
# Reports the approximate distance in time between two Time, Date or DateTime objects or integers as seconds.
-
# Set <tt>include_seconds</tt> to true if you want more detailed approximations when distance < 1 min, 29 secs.
-
# Distances are reported based on the following table:
-
#
-
# 0 <-> 29 secs # => less than a minute
-
# 30 secs <-> 1 min, 29 secs # => 1 minute
-
# 1 min, 30 secs <-> 44 mins, 29 secs # => [2..44] minutes
-
# 44 mins, 30 secs <-> 89 mins, 29 secs # => about 1 hour
-
# 89 mins, 30 secs <-> 23 hrs, 59 mins, 29 secs # => about [2..24] hours
-
# 23 hrs, 59 mins, 30 secs <-> 41 hrs, 59 mins, 29 secs # => 1 day
-
# 41 hrs, 59 mins, 30 secs <-> 29 days, 23 hrs, 59 mins, 29 secs # => [2..29] days
-
# 29 days, 23 hrs, 59 mins, 30 secs <-> 59 days, 23 hrs, 59 mins, 29 secs # => about 1 month
-
# 59 days, 23 hrs, 59 mins, 30 secs <-> 1 yr minus 1 sec # => [2..12] months
-
# 1 yr <-> 1 yr, 3 months # => about 1 year
-
# 1 yr, 3 months <-> 1 yr, 9 months # => over 1 year
-
# 1 yr, 9 months <-> 2 yr minus 1 sec # => almost 2 years
-
# 2 yrs <-> max time or date # => (same rules as 1 yr)
-
#
-
# With <tt>include_seconds</tt> = true and the difference < 1 minute 29 seconds:
-
# 0-4 secs # => less than 5 seconds
-
# 5-9 secs # => less than 10 seconds
-
# 10-19 secs # => less than 20 seconds
-
# 20-39 secs # => half a minute
-
# 40-59 secs # => less than a minute
-
# 60-89 secs # => 1 minute
-
#
-
# ==== Examples
-
# from_time = Time.now
-
# distance_of_time_in_words(from_time, from_time + 50.minutes) # => about 1 hour
-
# distance_of_time_in_words(from_time, 50.minutes.from_now) # => about 1 hour
-
# distance_of_time_in_words(from_time, from_time + 15.seconds) # => less than a minute
-
# distance_of_time_in_words(from_time, from_time + 15.seconds, true) # => less than 20 seconds
-
# distance_of_time_in_words(from_time, 3.years.from_now) # => about 3 years
-
# distance_of_time_in_words(from_time, from_time + 60.hours) # => 3 days
-
# distance_of_time_in_words(from_time, from_time + 45.seconds, true) # => less than a minute
-
# distance_of_time_in_words(from_time, from_time - 45.seconds, true) # => less than a minute
-
# distance_of_time_in_words(from_time, 76.seconds.from_now) # => 1 minute
-
# distance_of_time_in_words(from_time, from_time + 1.year + 3.days) # => about 1 year
-
# distance_of_time_in_words(from_time, from_time + 3.years + 6.months) # => over 3 years
-
# distance_of_time_in_words(from_time, from_time + 4.years + 9.days + 30.minutes + 5.seconds) # => about 4 years
-
#
-
# to_time = Time.now + 6.years + 19.days
-
# distance_of_time_in_words(from_time, to_time, true) # => about 6 years
-
# distance_of_time_in_words(to_time, from_time, true) # => about 6 years
-
# distance_of_time_in_words(Time.now, Time.now) # => less than a minute
-
#
-
1
def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false, options = {})
-
from_time = from_time.to_time if from_time.respond_to?(:to_time)
-
to_time = to_time.to_time if to_time.respond_to?(:to_time)
-
distance_in_minutes = (((to_time - from_time).abs)/60).round
-
distance_in_seconds = ((to_time - from_time).abs).round
-
-
I18n.with_options :locale => options[:locale], :scope => :'datetime.distance_in_words' do |locale|
-
case distance_in_minutes
-
when 0..1
-
return distance_in_minutes == 0 ?
-
locale.t(:less_than_x_minutes, :count => 1) :
-
locale.t(:x_minutes, :count => distance_in_minutes) unless include_seconds
-
-
case distance_in_seconds
-
when 0..4 then locale.t :less_than_x_seconds, :count => 5
-
when 5..9 then locale.t :less_than_x_seconds, :count => 10
-
when 10..19 then locale.t :less_than_x_seconds, :count => 20
-
when 20..39 then locale.t :half_a_minute
-
when 40..59 then locale.t :less_than_x_minutes, :count => 1
-
else locale.t :x_minutes, :count => 1
-
end
-
-
when 2..44 then locale.t :x_minutes, :count => distance_in_minutes
-
when 45..89 then locale.t :about_x_hours, :count => 1
-
when 90..1439 then locale.t :about_x_hours, :count => (distance_in_minutes.to_f / 60.0).round
-
when 1440..2519 then locale.t :x_days, :count => 1
-
when 2520..43199 then locale.t :x_days, :count => (distance_in_minutes.to_f / 1440.0).round
-
when 43200..86399 then locale.t :about_x_months, :count => 1
-
when 86400..525599 then locale.t :x_months, :count => (distance_in_minutes.to_f / 43200.0).round
-
else
-
fyear = from_time.year
-
fyear += 1 if from_time.month >= 3
-
tyear = to_time.year
-
tyear -= 1 if to_time.month < 3
-
leap_years = (fyear > tyear) ? 0 : (fyear..tyear).count{|x| Date.leap?(x)}
-
minute_offset_for_leap_year = leap_years * 1440
-
# Discount the leap year days when calculating year distance.
-
# e.g. if there are 20 leap year days between 2 dates having the same day
-
# and month then the based on 365 days calculation
-
# the distance in years will come out to over 80 years when in written
-
# english it would read better as about 80 years.
-
minutes_with_offset = distance_in_minutes - minute_offset_for_leap_year
-
remainder = (minutes_with_offset % 525600)
-
distance_in_years = (minutes_with_offset / 525600)
-
if remainder < 131400
-
locale.t(:about_x_years, :count => distance_in_years)
-
elsif remainder < 394200
-
locale.t(:over_x_years, :count => distance_in_years)
-
else
-
locale.t(:almost_x_years, :count => distance_in_years + 1)
-
end
-
end
-
end
-
end
-
-
# Like <tt>distance_of_time_in_words</tt>, but where <tt>to_time</tt> is fixed to <tt>Time.now</tt>.
-
#
-
# ==== Examples
-
# time_ago_in_words(3.minutes.from_now) # => 3 minutes
-
# time_ago_in_words(Time.now - 15.hours) # => about 15 hours
-
# time_ago_in_words(Time.now) # => less than a minute
-
#
-
# from_time = Time.now - 3.days - 14.minutes - 25.seconds
-
# time_ago_in_words(from_time) # => 3 days
-
#
-
1
def time_ago_in_words(from_time, include_seconds = false)
-
distance_of_time_in_words(from_time, Time.now, include_seconds)
-
end
-
-
1
alias_method :distance_of_time_in_words_to_now, :time_ago_in_words
-
-
# Returns a set of select tags (one for year, month, and day) pre-selected for accessing a specified date-based
-
# attribute (identified by +method+) on an object assigned to the template (identified by +object+).
-
#
-
#
-
# ==== Options
-
# * <tt>:use_month_numbers</tt> - Set to true if you want to use month numbers rather than month names (e.g.
-
# "2" instead of "February").
-
# * <tt>:use_short_month</tt> - Set to true if you want to use abbreviated month names instead of full
-
# month names (e.g. "Feb" instead of "February").
-
# * <tt>:add_month_numbers</tt> - Set to true if you want to use both month numbers and month names (e.g.
-
# "2 - February" instead of "February").
-
# * <tt>:use_month_names</tt> - Set to an array with 12 month names if you want to customize month names.
-
# Note: You can also use Rails' i18n functionality for this.
-
# * <tt>:date_separator</tt> - Specifies a string to separate the date fields. Default is "" (i.e. nothing).
-
# * <tt>:start_year</tt> - Set the start year for the year select. Default is <tt>Time.now.year - 5</tt>.
-
# * <tt>:end_year</tt> - Set the end year for the year select. Default is <tt>Time.now.year + 5</tt>.
-
# * <tt>:discard_day</tt> - Set to true if you don't want to show a day select. This includes the day
-
# as a hidden field instead of showing a select field. Also note that this implicitly sets the day to be the
-
# first of the given month in order to not create invalid dates like 31 February.
-
# * <tt>:discard_month</tt> - Set to true if you don't want to show a month select. This includes the month
-
# as a hidden field instead of showing a select field. Also note that this implicitly sets :discard_day to true.
-
# * <tt>:discard_year</tt> - Set to true if you don't want to show a year select. This includes the year
-
# as a hidden field instead of showing a select field.
-
# * <tt>:order</tt> - Set to an array containing <tt>:day</tt>, <tt>:month</tt> and <tt>:year</tt> to
-
# customize the order in which the select fields are shown. If you leave out any of the symbols, the respective
-
# select will not be shown (like when you set <tt>:discard_xxx => true</tt>. Defaults to the order defined in
-
# the respective locale (e.g. [:year, :month, :day] in the en locale that ships with Rails).
-
# * <tt>:include_blank</tt> - Include a blank option in every select field so it's possible to set empty
-
# dates.
-
# * <tt>:default</tt> - Set a default date if the affected date isn't set or is nil.
-
# * <tt>:disabled</tt> - Set to true if you want show the select fields as disabled.
-
# * <tt>:prompt</tt> - Set to true (for a generic prompt), a prompt string or a hash of prompt strings
-
# for <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:minute</tt> and <tt>:second</tt>.
-
# Setting this option prepends a select option with a generic prompt (Day, Month, Year, Hour, Minute, Seconds)
-
# or the given prompt string.
-
#
-
# If anything is passed in the +html_options+ hash it will be applied to every select tag in the set.
-
#
-
# NOTE: Discarded selects will default to 1. So if no month select is available, January will be assumed.
-
#
-
# ==== Examples
-
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute.
-
# date_select("article", "written_on")
-
#
-
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute,
-
# # with the year in the year drop down box starting at 1995.
-
# date_select("article", "written_on", :start_year => 1995)
-
#
-
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute,
-
# # with the year in the year drop down box starting at 1995, numbers used for months instead of words,
-
# # and without a day select box.
-
# date_select("article", "written_on", :start_year => 1995, :use_month_numbers => true,
-
# :discard_day => true, :include_blank => true)
-
#
-
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
-
# # with the fields ordered as day, month, year rather than month, day, year.
-
# date_select("article", "written_on", :order => [:day, :month, :year])
-
#
-
# # Generates a date select that when POSTed is stored in the user variable, in the birthday attribute
-
# # lacking a year field.
-
# date_select("user", "birthday", :order => [:month, :day])
-
#
-
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
-
# # which is initially set to the date 3 days from the current date
-
# date_select("article", "written_on", :default => 3.days.from_now)
-
#
-
# # Generates a date select that when POSTed is stored in the credit_card variable, in the bill_due attribute
-
# # that will have a default day of 20.
-
# date_select("credit_card", "bill_due", :default => { :day => 20 })
-
#
-
# # Generates a date select with custom prompts.
-
# date_select("article", "written_on", :prompt => { :day => 'Select day', :month => 'Select month', :year => 'Select year' })
-
#
-
# The selects are prepared for multi-parameter assignment to an Active Record object.
-
#
-
# Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
-
# all month choices are valid.
-
1
def date_select(object_name, method, options = {}, html_options = {})
-
InstanceTag.new(object_name, method, self, options.delete(:object)).to_date_select_tag(options, html_options)
-
end
-
-
# Returns a set of select tags (one for hour, minute and optionally second) pre-selected for accessing a
-
# specified time-based attribute (identified by +method+) on an object assigned to the template (identified by
-
# +object+). You can include the seconds with <tt>:include_seconds</tt>. You can get hours in the AM/PM format
-
# with <tt>:ampm</tt> option.
-
#
-
# This method will also generate 3 input hidden tags, for the actual year, month and day unless the option
-
# <tt>:ignore_date</tt> is set to +true+. If you set the <tt>:ignore_date</tt> to +true+, you must have a
-
# +date_select+ on the same method within the form otherwise an exception will be raised.
-
#
-
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
-
#
-
# ==== Examples
-
# # Creates a time select tag that, when POSTed, will be stored in the article variable in the sunrise attribute.
-
# time_select("article", "sunrise")
-
#
-
# # Creates a time select tag with a seconds field that, when POSTed, will be stored in the article variables in
-
# # the sunrise attribute.
-
# time_select("article", "start_time", :include_seconds => true)
-
#
-
# # You can set the <tt>:minute_step</tt> to 15 which will give you: 00, 15, 30 and 45.
-
# time_select 'game', 'game_time', {:minute_step => 15}
-
#
-
# # Creates a time select tag with a custom prompt. Use <tt>:prompt => true</tt> for generic prompts.
-
# time_select("article", "written_on", :prompt => {:hour => 'Choose hour', :minute => 'Choose minute', :second => 'Choose seconds'})
-
# time_select("article", "written_on", :prompt => {:hour => true}) # generic prompt for hours
-
# time_select("article", "written_on", :prompt => true) # generic prompts for all
-
#
-
# # You can set :ampm option to true which will show the hours as: 12 PM, 01 AM .. 11 PM.
-
# time_select 'game', 'game_time', {:ampm => true}
-
#
-
# The selects are prepared for multi-parameter assignment to an Active Record object.
-
#
-
# Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
-
# all month choices are valid.
-
1
def time_select(object_name, method, options = {}, html_options = {})
-
InstanceTag.new(object_name, method, self, options.delete(:object)).to_time_select_tag(options, html_options)
-
end
-
-
# Returns a set of select tags (one for year, month, day, hour, and minute) pre-selected for accessing a
-
# specified datetime-based attribute (identified by +method+) on an object assigned to the template (identified
-
# by +object+).
-
#
-
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
-
#
-
# ==== Examples
-
# # Generates a datetime select that, when POSTed, will be stored in the article variable in the written_on
-
# # attribute.
-
# datetime_select("article", "written_on")
-
#
-
# # Generates a datetime select with a year select that starts at 1995 that, when POSTed, will be stored in the
-
# # article variable in the written_on attribute.
-
# datetime_select("article", "written_on", :start_year => 1995)
-
#
-
# # Generates a datetime select with a default value of 3 days from the current time that, when POSTed, will
-
# # be stored in the trip variable in the departing attribute.
-
# datetime_select("trip", "departing", :default => 3.days.from_now)
-
#
-
# # Generate a datetime select with hours in the AM/PM format
-
# datetime_select("article", "written_on", :ampm => true)
-
#
-
# # Generates a datetime select that discards the type that, when POSTed, will be stored in the article variable
-
# # as the written_on attribute.
-
# datetime_select("article", "written_on", :discard_type => true)
-
#
-
# # Generates a datetime select with a custom prompt. Use <tt>:prompt => true</tt> for generic prompts.
-
# datetime_select("article", "written_on", :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year'})
-
# datetime_select("article", "written_on", :prompt => {:hour => true}) # generic prompt for hours
-
# datetime_select("article", "written_on", :prompt => true) # generic prompts for all
-
#
-
# The selects are prepared for multi-parameter assignment to an Active Record object.
-
1
def datetime_select(object_name, method, options = {}, html_options = {})
-
InstanceTag.new(object_name, method, self, options.delete(:object)).to_datetime_select_tag(options, html_options)
-
end
-
-
# Returns a set of html select-tags (one for year, month, day, hour, minute and second) pre-selected with the
-
# +datetime+. It's also possible to explicitly set the order of the tags using the <tt>:order</tt> option with
-
# an array of symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order. If you do not
-
# supply a Symbol, it will be appended onto the <tt>:order</tt> passed in. You can also add
-
# <tt>:date_separator</tt>, <tt>:datetime_separator</tt> and <tt>:time_separator</tt> keys to the +options+ to
-
# control visual display of the elements.
-
#
-
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
-
#
-
# ==== Examples
-
# my_date_time = Time.now + 4.days
-
#
-
# # Generates a datetime select that defaults to the datetime in my_date_time (four days after today).
-
# select_datetime(my_date_time)
-
#
-
# # Generates a datetime select that defaults to today (no specified datetime)
-
# select_datetime()
-
#
-
# # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
-
# # with the fields ordered year, month, day rather than month, day, year.
-
# select_datetime(my_date_time, :order => [:year, :month, :day])
-
#
-
# # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
-
# # with a '/' between each date field.
-
# select_datetime(my_date_time, :date_separator => '/')
-
#
-
# # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
-
# # with a date fields separated by '/', time fields separated by '' and the date and time fields
-
# # separated by a comma (',').
-
# select_datetime(my_date_time, :date_separator => '/', :time_separator => '', :datetime_separator => ',')
-
#
-
# # Generates a datetime select that discards the type of the field and defaults to the datetime in
-
# # my_date_time (four days after today)
-
# select_datetime(my_date_time, :discard_type => true)
-
#
-
# # Generate a datetime field with hours in the AM/PM format
-
# select_datetime(my_date_time, :ampm => true)
-
#
-
# # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
-
# # prefixed with 'payday' rather than 'date'
-
# select_datetime(my_date_time, :prefix => 'payday')
-
#
-
# # Generates a datetime select with a custom prompt. Use <tt>:prompt => true</tt> for generic prompts.
-
# select_datetime(my_date_time, :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year'})
-
# select_datetime(my_date_time, :prompt => {:hour => true}) # generic prompt for hours
-
# select_datetime(my_date_time, :prompt => true) # generic prompts for all
-
#
-
1
def select_datetime(datetime = Time.current, options = {}, html_options = {})
-
DateTimeSelector.new(datetime, options, html_options).select_datetime
-
end
-
-
# Returns a set of html select-tags (one for year, month, and day) pre-selected with the +date+.
-
# It's possible to explicitly set the order of the tags using the <tt>:order</tt> option with an array of
-
# symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order.
-
# If the array passed to the <tt>:order</tt> option does not contain all the three symbols, all tags will be hidden.
-
#
-
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
-
#
-
# ==== Examples
-
# my_date = Time.now + 6.days
-
#
-
# # Generates a date select that defaults to the date in my_date (six days after today).
-
# select_date(my_date)
-
#
-
# # Generates a date select that defaults to today (no specified date).
-
# select_date()
-
#
-
# # Generates a date select that defaults to the date in my_date (six days after today)
-
# # with the fields ordered year, month, day rather than month, day, year.
-
# select_date(my_date, :order => [:year, :month, :day])
-
#
-
# # Generates a date select that discards the type of the field and defaults to the date in
-
# # my_date (six days after today).
-
# select_date(my_date, :discard_type => true)
-
#
-
# # Generates a date select that defaults to the date in my_date,
-
# # which has fields separated by '/'.
-
# select_date(my_date, :date_separator => '/')
-
#
-
# # Generates a date select that defaults to the datetime in my_date (six days after today)
-
# # prefixed with 'payday' rather than 'date'.
-
# select_date(my_date, :prefix => 'payday')
-
#
-
# # Generates a date select with a custom prompt. Use <tt>:prompt => true</tt> for generic prompts.
-
# select_date(my_date, :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year'})
-
# select_date(my_date, :prompt => {:hour => true}) # generic prompt for hours
-
# select_date(my_date, :prompt => true) # generic prompts for all
-
#
-
1
def select_date(date = Date.current, options = {}, html_options = {})
-
DateTimeSelector.new(date, options, html_options).select_date
-
end
-
-
# Returns a set of html select-tags (one for hour and minute).
-
# You can set <tt>:time_separator</tt> key to format the output, and
-
# the <tt>:include_seconds</tt> option to include an input for seconds.
-
#
-
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
-
#
-
# ==== Examples
-
# my_time = Time.now + 5.days + 7.hours + 3.minutes + 14.seconds
-
#
-
# # Generates a time select that defaults to the time in my_time.
-
# select_time(my_time)
-
#
-
# # Generates a time select that defaults to the current time (no specified time).
-
# select_time()
-
#
-
# # Generates a time select that defaults to the time in my_time,
-
# # which has fields separated by ':'.
-
# select_time(my_time, :time_separator => ':')
-
#
-
# # Generates a time select that defaults to the time in my_time,
-
# # that also includes an input for seconds.
-
# select_time(my_time, :include_seconds => true)
-
#
-
# # Generates a time select that defaults to the time in my_time, that has fields
-
# # separated by ':' and includes an input for seconds.
-
# select_time(my_time, :time_separator => ':', :include_seconds => true)
-
#
-
# # Generate a time select field with hours in the AM/PM format
-
# select_time(my_time, :ampm => true)
-
#
-
# # Generates a time select with a custom prompt. Use <tt>:prompt</tt> to true for generic prompts.
-
# select_time(my_time, :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year'})
-
# select_time(my_time, :prompt => {:hour => true}) # generic prompt for hours
-
# select_time(my_time, :prompt => true) # generic prompts for all
-
#
-
1
def select_time(datetime = Time.current, options = {}, html_options = {})
-
DateTimeSelector.new(datetime, options, html_options).select_time
-
end
-
-
# Returns a select tag with options for each of the seconds 0 through 59 with the current second selected.
-
# The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
-
# Override the field name using the <tt>:field_name</tt> option, 'second' by default.
-
#
-
# ==== Examples
-
# my_time = Time.now + 16.minutes
-
#
-
# # Generates a select field for seconds that defaults to the seconds for the time in my_time.
-
# select_second(my_time)
-
#
-
# # Generates a select field for seconds that defaults to the number given.
-
# select_second(33)
-
#
-
# # Generates a select field for seconds that defaults to the seconds for the time in my_time
-
# # that is named 'interval' rather than 'second'.
-
# select_second(my_time, :field_name => 'interval')
-
#
-
# # Generates a select field for seconds with a custom prompt. Use <tt>:prompt => true</tt> for a
-
# # generic prompt.
-
# select_second(14, :prompt => 'Choose seconds')
-
#
-
1
def select_second(datetime, options = {}, html_options = {})
-
DateTimeSelector.new(datetime, options, html_options).select_second
-
end
-
-
# Returns a select tag with options for each of the minutes 0 through 59 with the current minute selected.
-
# Also can return a select tag with options by <tt>minute_step</tt> from 0 through 59 with the 00 minute
-
# selected. The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
-
# Override the field name using the <tt>:field_name</tt> option, 'minute' by default.
-
#
-
# ==== Examples
-
# my_time = Time.now + 6.hours
-
#
-
# # Generates a select field for minutes that defaults to the minutes for the time in my_time.
-
# select_minute(my_time)
-
#
-
# # Generates a select field for minutes that defaults to the number given.
-
# select_minute(14)
-
#
-
# # Generates a select field for minutes that defaults to the minutes for the time in my_time
-
# # that is named 'moment' rather than 'minute'.
-
# select_minute(my_time, :field_name => 'moment')
-
#
-
# # Generates a select field for minutes with a custom prompt. Use <tt>:prompt => true</tt> for a
-
# # generic prompt.
-
# select_minute(14, :prompt => 'Choose minutes')
-
#
-
1
def select_minute(datetime, options = {}, html_options = {})
-
DateTimeSelector.new(datetime, options, html_options).select_minute
-
end
-
-
# Returns a select tag with options for each of the hours 0 through 23 with the current hour selected.
-
# The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
-
# Override the field name using the <tt>:field_name</tt> option, 'hour' by default.
-
#
-
# ==== Examples
-
# my_time = Time.now + 6.hours
-
#
-
# # Generates a select field for hours that defaults to the hour for the time in my_time.
-
# select_hour(my_time)
-
#
-
# # Generates a select field for hours that defaults to the number given.
-
# select_hour(13)
-
#
-
# # Generates a select field for hours that defaults to the hour for the time in my_time
-
# # that is named 'stride' rather than 'hour'.
-
# select_hour(my_time, :field_name => 'stride')
-
#
-
# # Generates a select field for hours with a custom prompt. Use <tt>:prompt => true</tt> for a
-
# # generic prompt.
-
# select_hour(13, :prompt => 'Choose hour')
-
#
-
# # Generate a select field for hours in the AM/PM format
-
# select_hour(my_time, :ampm => true)
-
#
-
1
def select_hour(datetime, options = {}, html_options = {})
-
DateTimeSelector.new(datetime, options, html_options).select_hour
-
end
-
-
# Returns a select tag with options for each of the days 1 through 31 with the current day selected.
-
# The <tt>date</tt> can also be substituted for a day number.
-
# Override the field name using the <tt>:field_name</tt> option, 'day' by default.
-
#
-
# ==== Examples
-
# my_date = Time.now + 2.days
-
#
-
# # Generates a select field for days that defaults to the day for the date in my_date.
-
# select_day(my_time)
-
#
-
# # Generates a select field for days that defaults to the number given.
-
# select_day(5)
-
#
-
# # Generates a select field for days that defaults to the day for the date in my_date
-
# # that is named 'due' rather than 'day'.
-
# select_day(my_time, :field_name => 'due')
-
#
-
# # Generates a select field for days with a custom prompt. Use <tt>:prompt => true</tt> for a
-
# # generic prompt.
-
# select_day(5, :prompt => 'Choose day')
-
#
-
1
def select_day(date, options = {}, html_options = {})
-
DateTimeSelector.new(date, options, html_options).select_day
-
end
-
-
# Returns a select tag with options for each of the months January through December with the current month
-
# selected. The month names are presented as keys (what's shown to the user) and the month numbers (1-12) are
-
# used as values (what's submitted to the server). It's also possible to use month numbers for the presentation
-
# instead of names -- set the <tt>:use_month_numbers</tt> key in +options+ to true for this to happen. If you
-
# want both numbers and names, set the <tt>:add_month_numbers</tt> key in +options+ to true. If you would prefer
-
# to show month names as abbreviations, set the <tt>:use_short_month</tt> key in +options+ to true. If you want
-
# to use your own month names, set the <tt>:use_month_names</tt> key in +options+ to an array of 12 month names.
-
# Override the field name using the <tt>:field_name</tt> option, 'month' by default.
-
#
-
# ==== Examples
-
# # Generates a select field for months that defaults to the current month that
-
# # will use keys like "January", "March".
-
# select_month(Date.today)
-
#
-
# # Generates a select field for months that defaults to the current month that
-
# # is named "start" rather than "month".
-
# select_month(Date.today, :field_name => 'start')
-
#
-
# # Generates a select field for months that defaults to the current month that
-
# # will use keys like "1", "3".
-
# select_month(Date.today, :use_month_numbers => true)
-
#
-
# # Generates a select field for months that defaults to the current month that
-
# # will use keys like "1 - January", "3 - March".
-
# select_month(Date.today, :add_month_numbers => true)
-
#
-
# # Generates a select field for months that defaults to the current month that
-
# # will use keys like "Jan", "Mar".
-
# select_month(Date.today, :use_short_month => true)
-
#
-
# # Generates a select field for months that defaults to the current month that
-
# # will use keys like "Januar", "Marts."
-
# select_month(Date.today, :use_month_names => %w(Januar Februar Marts ...))
-
#
-
# # Generates a select field for months with a custom prompt. Use <tt>:prompt => true</tt> for a
-
# # generic prompt.
-
# select_month(14, :prompt => 'Choose month')
-
#
-
1
def select_month(date, options = {}, html_options = {})
-
DateTimeSelector.new(date, options, html_options).select_month
-
end
-
-
# Returns a select tag with options for each of the five years on each side of the current, which is selected.
-
# The five year radius can be changed using the <tt>:start_year</tt> and <tt>:end_year</tt> keys in the
-
# +options+. Both ascending and descending year lists are supported by making <tt>:start_year</tt> less than or
-
# greater than <tt>:end_year</tt>. The <tt>date</tt> can also be substituted for a year given as a number.
-
# Override the field name using the <tt>:field_name</tt> option, 'year' by default.
-
#
-
# ==== Examples
-
# # Generates a select field for years that defaults to the current year that
-
# # has ascending year values.
-
# select_year(Date.today, :start_year => 1992, :end_year => 2007)
-
#
-
# # Generates a select field for years that defaults to the current year that
-
# # is named 'birth' rather than 'year'.
-
# select_year(Date.today, :field_name => 'birth')
-
#
-
# # Generates a select field for years that defaults to the current year that
-
# # has descending year values.
-
# select_year(Date.today, :start_year => 2005, :end_year => 1900)
-
#
-
# # Generates a select field for years that defaults to the year 2006 that
-
# # has ascending year values.
-
# select_year(2006, :start_year => 2000, :end_year => 2010)
-
#
-
# # Generates a select field for years with a custom prompt. Use <tt>:prompt => true</tt> for a
-
# # generic prompt.
-
# select_year(14, :prompt => 'Choose year')
-
#
-
1
def select_year(date, options = {}, html_options = {})
-
DateTimeSelector.new(date, options, html_options).select_year
-
end
-
-
# Returns an html time tag for the given date or time.
-
#
-
# ==== Examples
-
# time_tag Date.today # =>
-
# <time datetime="2010-11-04">November 04, 2010</time>
-
# time_tag Time.now # =>
-
# <time datetime="2010-11-04T17:55:45+01:00">November 04, 2010 17:55</time>
-
# time_tag Date.yesterday, 'Yesterday' # =>
-
# <time datetime="2010-11-03">Yesterday</time>
-
# time_tag Date.today, :pubdate => true # =>
-
# <time datetime="2010-11-04" pubdate="pubdate">November 04, 2010</time>
-
#
-
1
def time_tag(date_or_time, *args)
-
options = args.extract_options!
-
format = options.delete(:format) || :long
-
content = args.first || I18n.l(date_or_time, :format => format)
-
datetime = date_or_time.acts_like?(:time) ? date_or_time.xmlschema : date_or_time.rfc3339
-
-
content_tag(:time, content, options.reverse_merge(:datetime => datetime))
-
end
-
end
-
-
1
class DateTimeSelector #:nodoc:
-
1
extend ActiveSupport::Memoizable
-
1
include ActionView::Helpers::TagHelper
-
-
1
DEFAULT_PREFIX = 'date'.freeze
-
1
POSITION = {
-
:year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6
-
}.freeze
-
-
1
AMPM_TRANSLATION = Hash[
-
[[0, "12 AM"], [1, "01 AM"], [2, "02 AM"], [3, "03 AM"],
-
[4, "04 AM"], [5, "05 AM"], [6, "06 AM"], [7, "07 AM"],
-
[8, "08 AM"], [9, "09 AM"], [10, "10 AM"], [11, "11 AM"],
-
[12, "12 PM"], [13, "01 PM"], [14, "02 PM"], [15, "03 PM"],
-
[16, "04 PM"], [17, "05 PM"], [18, "06 PM"], [19, "07 PM"],
-
[20, "08 PM"], [21, "09 PM"], [22, "10 PM"], [23, "11 PM"]]
-
].freeze
-
-
1
def initialize(datetime, options = {}, html_options = {})
-
@options = options.dup
-
@html_options = html_options.dup
-
@datetime = datetime
-
@options[:datetime_separator] ||= ' — '
-
@options[:time_separator] ||= ' : '
-
end
-
-
1
def select_datetime
-
order = date_order.dup
-
order -= [:hour, :minute, :second]
-
@options[:discard_year] ||= true unless order.include?(:year)
-
@options[:discard_month] ||= true unless order.include?(:month)
-
@options[:discard_day] ||= true if @options[:discard_month] || !order.include?(:day)
-
@options[:discard_minute] ||= true if @options[:discard_hour]
-
@options[:discard_second] ||= true unless @options[:include_seconds] && !@options[:discard_minute]
-
-
# If the day is hidden and the month is visible, the day should be set to the 1st so all month choices are
-
# valid (otherwise it could be 31 and February wouldn't be a valid date)
-
if @datetime && @options[:discard_day] && !@options[:discard_month]
-
@datetime = @datetime.change(:day => 1)
-
end
-
-
if @options[:tag] && @options[:ignore_date]
-
select_time
-
else
-
[:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
-
order += [:hour, :minute, :second] unless @options[:discard_hour]
-
-
build_selects_from_types(order)
-
end
-
end
-
-
1
def select_date
-
order = date_order.dup
-
-
@options[:discard_hour] = true
-
@options[:discard_minute] = true
-
@options[:discard_second] = true
-
-
@options[:discard_year] ||= true unless order.include?(:year)
-
@options[:discard_month] ||= true unless order.include?(:month)
-
@options[:discard_day] ||= true if @options[:discard_month] || !order.include?(:day)
-
-
# If the day is hidden and the month is visible, the day should be set to the 1st so all month choices are
-
# valid (otherwise it could be 31 and February wouldn't be a valid date)
-
if @datetime && @options[:discard_day] && !@options[:discard_month]
-
@datetime = @datetime.change(:day => 1)
-
end
-
-
[:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
-
-
build_selects_from_types(order)
-
end
-
-
1
def select_time
-
order = []
-
-
@options[:discard_month] = true
-
@options[:discard_year] = true
-
@options[:discard_day] = true
-
@options[:discard_second] ||= true unless @options[:include_seconds]
-
-
order += [:year, :month, :day] unless @options[:ignore_date]
-
-
order += [:hour, :minute]
-
order << :second if @options[:include_seconds]
-
-
build_selects_from_types(order)
-
end
-
-
1
def select_second
-
if @options[:use_hidden] || @options[:discard_second]
-
build_hidden(:second, sec) if @options[:include_seconds]
-
else
-
build_options_and_select(:second, sec)
-
end
-
end
-
-
1
def select_minute
-
if @options[:use_hidden] || @options[:discard_minute]
-
build_hidden(:minute, min)
-
else
-
build_options_and_select(:minute, min, :step => @options[:minute_step])
-
end
-
end
-
-
1
def select_hour
-
if @options[:use_hidden] || @options[:discard_hour]
-
build_hidden(:hour, hour)
-
else
-
build_options_and_select(:hour, hour, :end => 23, :ampm => @options[:ampm])
-
end
-
end
-
-
1
def select_day
-
if @options[:use_hidden] || @options[:discard_day]
-
build_hidden(:day, day)
-
else
-
build_options_and_select(:day, day, :start => 1, :end => 31, :leading_zeros => false)
-
end
-
end
-
-
1
def select_month
-
if @options[:use_hidden] || @options[:discard_month]
-
build_hidden(:month, month)
-
else
-
month_options = []
-
1.upto(12) do |month_number|
-
options = { :value => month_number }
-
options[:selected] = "selected" if month == month_number
-
month_options << content_tag(:option, month_name(month_number), options) + "\n"
-
end
-
build_select(:month, month_options.join)
-
end
-
end
-
-
1
def select_year
-
if !@datetime || @datetime == 0
-
val = ''
-
middle_year = Date.today.year
-
else
-
val = middle_year = year
-
end
-
-
if @options[:use_hidden] || @options[:discard_year]
-
build_hidden(:year, val)
-
else
-
options = {}
-
options[:start] = @options[:start_year] || middle_year - 5
-
options[:end] = @options[:end_year] || middle_year + 5
-
options[:step] = options[:start] < options[:end] ? 1 : -1
-
options[:leading_zeros] = false
-
-
build_options_and_select(:year, val, options)
-
end
-
end
-
-
1
private
-
1
%w( sec min hour day month year ).each do |method|
-
6
define_method(method) do
-
@datetime.kind_of?(Fixnum) ? @datetime : @datetime.send(method) if @datetime
-
end
-
end
-
-
# Returns translated month names, but also ensures that a custom month
-
# name array has a leading nil element.
-
1
def month_names
-
month_names = @options[:use_month_names] || translated_month_names
-
month_names.unshift(nil) if month_names.size < 13
-
month_names
-
end
-
1
memoize :month_names
-
-
# Returns translated month names.
-
# => [nil, "January", "February", "March",
-
# "April", "May", "June", "July",
-
# "August", "September", "October",
-
# "November", "December"]
-
#
-
# If <tt>:use_short_month</tt> option is set
-
# => [nil, "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-
# "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
-
1
def translated_month_names
-
key = @options[:use_short_month] ? :'date.abbr_month_names' : :'date.month_names'
-
I18n.translate(key, :locale => @options[:locale])
-
end
-
-
# Lookup month name for number.
-
# month_name(1) => "January"
-
#
-
# If <tt>:use_month_numbers</tt> option is passed
-
# month_name(1) => 1
-
#
-
# If <tt>:add_month_numbers</tt> option is passed
-
# month_name(1) => "1 - January"
-
1
def month_name(number)
-
if @options[:use_month_numbers]
-
number
-
elsif @options[:add_month_numbers]
-
"#{number} - #{month_names[number]}"
-
else
-
month_names[number]
-
end
-
end
-
-
1
def date_order
-
@options[:order] || translated_date_order
-
end
-
1
memoize :date_order
-
-
1
def translated_date_order
-
I18n.translate(:'date.order', :locale => @options[:locale]) || []
-
end
-
-
# Build full select tag from date type and options.
-
1
def build_options_and_select(type, selected, options = {})
-
build_select(type, build_options(selected, options))
-
end
-
-
# Build select option html from date value and options.
-
# build_options(15, :start => 1, :end => 31)
-
# => "<option value="1">1</option>
-
# <option value="2">2</option>
-
# <option value="3">3</option>..."
-
#
-
# If <tt>:step</tt> options is passed
-
# build_options(15, :start => 1, :end => 31, :step => 2)
-
# => "<option value="1">1</option>
-
# <option value="3">3</option>
-
# <option value="5">5</option>..."
-
1
def build_options(selected, options = {})
-
start = options.delete(:start) || 0
-
stop = options.delete(:end) || 59
-
step = options.delete(:step) || 1
-
options.reverse_merge!({:leading_zeros => true, :ampm => false})
-
leading_zeros = options.delete(:leading_zeros)
-
-
select_options = []
-
start.step(stop, step) do |i|
-
value = leading_zeros ? sprintf("%02d", i) : i
-
tag_options = { :value => value }
-
tag_options[:selected] = "selected" if selected == i
-
text = options[:ampm] ? AMPM_TRANSLATION[i] : value
-
select_options << content_tag(:option, text, tag_options)
-
end
-
(select_options.join("\n") + "\n").html_safe
-
end
-
-
# Builds select tag from date type and html select options.
-
# build_select(:month, "<option value="1">January</option>...")
-
# => "<select id="post_written_on_2i" name="post[written_on(2i)]">
-
# <option value="1">January</option>...
-
# </select>"
-
1
def build_select(type, select_options_as_html)
-
select_options = {
-
:id => input_id_from_type(type),
-
:name => input_name_from_type(type)
-
}.merge(@html_options)
-
select_options.merge!(:disabled => 'disabled') if @options[:disabled]
-
-
select_html = "\n"
-
select_html << content_tag(:option, '', :value => '') + "\n" if @options[:include_blank]
-
select_html << prompt_option_tag(type, @options[:prompt]) + "\n" if @options[:prompt]
-
select_html << select_options_as_html
-
-
(content_tag(:select, select_html.html_safe, select_options) + "\n").html_safe
-
end
-
-
# Builds a prompt option tag with supplied options or from default options.
-
# prompt_option_tag(:month, :prompt => 'Select month')
-
# => "<option value="">Select month</option>"
-
1
def prompt_option_tag(type, options)
-
prompt = case options
-
when Hash
-
default_options = {:year => false, :month => false, :day => false, :hour => false, :minute => false, :second => false}
-
default_options.merge!(options)[type.to_sym]
-
when String
-
options
-
else
-
I18n.translate(:"datetime.prompts.#{type}", :locale => @options[:locale])
-
end
-
-
prompt ? content_tag(:option, prompt, :value => '') : ''
-
end
-
-
# Builds hidden input tag for date part and value.
-
# build_hidden(:year, 2008)
-
# => "<input id="post_written_on_1i" name="post[written_on(1i)]" type="hidden" value="2008" />"
-
1
def build_hidden(type, value)
-
(tag(:input, {
-
:type => "hidden",
-
:id => input_id_from_type(type),
-
:name => input_name_from_type(type),
-
:value => value
-
}.merge(@html_options.slice(:disabled))) + "\n").html_safe
-
end
-
-
# Returns the name attribute for the input tag.
-
# => post[written_on(1i)]
-
1
def input_name_from_type(type)
-
prefix = @options[:prefix] || ActionView::Helpers::DateTimeSelector::DEFAULT_PREFIX
-
prefix += "[#{@options[:index]}]" if @options.has_key?(:index)
-
-
field_name = @options[:field_name] || type
-
if @options[:include_position]
-
field_name += "(#{ActionView::Helpers::DateTimeSelector::POSITION[type]}i)"
-
end
-
-
@options[:discard_type] ? prefix : "#{prefix}[#{field_name}]"
-
end
-
-
# Returns the id attribute for the input tag.
-
# => "post_written_on_1i"
-
1
def input_id_from_type(type)
-
input_name_from_type(type).gsub(/([\[\(])|(\]\[)/, '_').gsub(/[\]\)]/, '')
-
end
-
-
# Given an ordering of datetime components, create the selection HTML
-
# and join them with their appropriate separators.
-
1
def build_selects_from_types(order)
-
select = ''
-
order.reverse.each do |type|
-
separator = separator(type) unless type == order.first # don't add on last field
-
select.insert(0, separator.to_s + send("select_#{type}").to_s)
-
end
-
select.html_safe
-
end
-
-
# Returns the separator for a given datetime component.
-
1
def separator(type)
-
case type
-
when :year
-
@options[:discard_year] ? "" : @options[:date_separator]
-
when :month
-
@options[:discard_month] ? "" : @options[:date_separator]
-
when :day
-
@options[:discard_day] ? "" : @options[:date_separator]
-
when :hour
-
(@options[:discard_year] && @options[:discard_day]) ? "" : @options[:datetime_separator]
-
when :minute
-
@options[:discard_minute] ? "" : @options[:time_separator]
-
when :second
-
@options[:include_seconds] ? @options[:time_separator] : ""
-
end
-
end
-
end
-
-
1
class InstanceTag #:nodoc:
-
1
def to_date_select_tag(options = {}, html_options = {})
-
datetime_selector(options, html_options).select_date.html_safe
-
end
-
-
1
def to_time_select_tag(options = {}, html_options = {})
-
datetime_selector(options, html_options).select_time.html_safe
-
end
-
-
1
def to_datetime_select_tag(options = {}, html_options = {})
-
datetime_selector(options, html_options).select_datetime.html_safe
-
end
-
-
1
private
-
1
def datetime_selector(options, html_options)
-
datetime = value(object) || default_datetime(options)
-
@auto_index ||= nil
-
-
options = options.dup
-
options[:field_name] = @method_name
-
options[:include_position] = true
-
options[:prefix] ||= @object_name
-
options[:index] = @auto_index if @auto_index && !options.has_key?(:index)
-
-
DateTimeSelector.new(datetime, options, html_options)
-
end
-
-
1
def default_datetime(options)
-
return if options[:include_blank] || options[:prompt]
-
-
case options[:default]
-
when nil
-
Time.current
-
when Date, Time
-
options[:default]
-
else
-
default = options[:default].dup
-
-
# Rename :minute and :second to :min and :sec
-
default[:min] ||= default[:minute]
-
default[:sec] ||= default[:second]
-
-
time = Time.current
-
-
[:year, :month, :day, :hour, :min, :sec].each do |key|
-
default[key] ||= time.send(key)
-
end
-
-
Time.utc_time(
-
default[:year], default[:month], default[:day],
-
default[:hour], default[:min], default[:sec]
-
)
-
end
-
end
-
end
-
-
1
class FormBuilder
-
1
def date_select(method, options = {}, html_options = {})
-
@template.date_select(@object_name, method, objectify_options(options), html_options)
-
end
-
-
1
def time_select(method, options = {}, html_options = {})
-
@template.time_select(@object_name, method, objectify_options(options), html_options)
-
end
-
-
1
def datetime_select(method, options = {}, html_options = {})
-
@template.datetime_select(@object_name, method, objectify_options(options), html_options)
-
end
-
end
-
end
-
end
-
1
module ActionView
-
# = Action View Debug Helper
-
#
-
# Provides a set of methods for making it easier to debug Rails objects.
-
1
module Helpers
-
1
module DebugHelper
-
# Returns a YAML representation of +object+ wrapped with <pre> and </pre>.
-
# If the object cannot be converted to YAML using +to_yaml+, +inspect+ will be called instead.
-
# Useful for inspecting an object at the time of rendering.
-
#
-
# ==== Example
-
#
-
# @user = User.new({ :username => 'testing', :password => 'xyz', :age => 42}) %>
-
# debug(@user)
-
# # =>
-
# <pre class='debug_dump'>--- !ruby/object:User
-
# attributes:
-
# updated_at:
-
# username: testing
-
#
-
# age: 42
-
# password: xyz
-
# created_at:
-
# attributes_cache: {}
-
#
-
# new_record: true
-
# </pre>
-
-
1
def debug(object)
-
begin
-
Marshal::dump(object)
-
"<pre class='debug_dump'>#{h(object.to_yaml).gsub(" ", " ")}</pre>".html_safe
-
rescue Exception => e # errors from Marshal or YAML
-
# Object couldn't be dumped, perhaps because of singleton methods -- this is the fallback
-
"<code class='debug_dump'>#{h(object.inspect)}</code>".html_safe
-
end
-
end
-
end
-
end
-
end
-
1
require 'cgi'
-
1
require 'action_view/helpers/date_helper'
-
1
require 'action_view/helpers/tag_helper'
-
1
require 'action_view/helpers/form_tag_helper'
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/hash/slice'
-
1
require 'active_support/core_ext/module/method_names'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/string/output_safety'
-
1
require 'active_support/core_ext/array/extract_options'
-
-
1
module ActionView
-
# = Action View Form Helpers
-
1
module Helpers
-
# Form helpers are designed to make working with resources much easier
-
# compared to using vanilla HTML.
-
#
-
# Forms for models are created with +form_for+. That method yields a form
-
# builder that knows the model the form is about. The form builder is thus
-
# able to generate default values for input fields that correspond to model
-
# attributes, and also convenient names, IDs, endpoints, etc.
-
#
-
# Conventions in the generated field names allow controllers to receive form
-
# data nicely structured in +params+ with no effort on your side.
-
#
-
# For example, to create a new person you typically set up a new instance of
-
# +Person+ in the <tt>PeopleController#new</tt> action, <tt>@person</tt>, and
-
# pass it to +form_for+:
-
#
-
# <%= form_for @person do |f| %>
-
# <%= f.label :first_name %>:
-
# <%= f.text_field :first_name %><br />
-
#
-
# <%= f.label :last_name %>:
-
# <%= f.text_field :last_name %><br />
-
#
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# The HTML generated for this would be (modulus formatting):
-
#
-
# <form action="/people" class="new_person" id="new_person" method="post">
-
# <div style="margin:0;padding:0;display:inline">
-
# <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
-
# </div>
-
# <label for="person_first_name">First name</label>:
-
# <input id="person_first_name" name="person[first_name]" size="30" type="text" /><br />
-
#
-
# <label for="person_last_name">Last name</label>:
-
# <input id="person_last_name" name="person[last_name]" size="30" type="text" /><br />
-
#
-
# <input name="commit" type="submit" value="Create Person" />
-
# </form>
-
#
-
# As you see, the HTML reflects knowledge about the resource in several spots,
-
# like the path the form should be submitted to, or the names of the input fields.
-
#
-
# In particular, thanks to the conventions followed in the generated field names, the
-
# controller gets a nested hash <tt>params[:person]</tt> with the person attributes
-
# set in the form. That hash is ready to be passed to <tt>Person.create</tt>:
-
#
-
# if @person = Person.create(params[:person])
-
# # success
-
# else
-
# # error handling
-
# end
-
#
-
# Interestingly, the exact same view code in the previous example can be used to edit
-
# a person. If <tt>@person</tt> is an existing record with name "John Smith" and ID 256,
-
# the code above as is would yield instead:
-
#
-
# <form action="/people/256" class="edit_person" id="edit_person_256" method="post">
-
# <div style="margin:0;padding:0;display:inline">
-
# <input name="_method" type="hidden" value="put" />
-
# <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
-
# </div>
-
# <label for="person_first_name">First name</label>:
-
# <input id="person_first_name" name="person[first_name]" size="30" type="text" value="John" /><br />
-
#
-
# <label for="person_last_name">Last name</label>:
-
# <input id="person_last_name" name="person[last_name]" size="30" type="text" value="Smith" /><br />
-
#
-
# <input name="commit" type="submit" value="Update Person" />
-
# </form>
-
#
-
# Note that the endpoint, default values, and submit button label are tailored for <tt>@person</tt>.
-
# That works that way because the involved helpers know whether the resource is a new record or not,
-
# and generate HTML accordingly.
-
#
-
# The controller would receive the form data again in <tt>params[:person]</tt>, ready to be
-
# passed to <tt>Person#update_attributes</tt>:
-
#
-
# if @person.update_attributes(params[:person])
-
# # success
-
# else
-
# # error handling
-
# end
-
#
-
# That's how you typically work with resources.
-
1
module FormHelper
-
1
extend ActiveSupport::Concern
-
-
1
include FormTagHelper
-
1
include UrlHelper
-
-
# Converts the given object to an ActiveModel compliant one.
-
1
def convert_to_model(object)
-
object.respond_to?(:to_model) ? object.to_model : object
-
end
-
-
# Creates a form and a scope around a specific model object that is used
-
# as a base for questioning about values for the fields.
-
#
-
# Rails provides succinct resource-oriented form generation with +form_for+
-
# like this:
-
#
-
# <%= form_for @offer do |f| %>
-
# <%= f.label :version, 'Version' %>:
-
# <%= f.text_field :version %><br />
-
# <%= f.label :author, 'Author' %>:
-
# <%= f.text_field :author %><br />
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# There, +form_for+ is able to generate the rest of RESTful form
-
# parameters based on introspection on the record, but to understand what
-
# it does we need to dig first into the alternative generic usage it is
-
# based upon.
-
#
-
# === Generic form_for
-
#
-
# The generic way to call +form_for+ yields a form builder around a
-
# model:
-
#
-
# <%= form_for :person do |f| %>
-
# First name: <%= f.text_field :first_name %><br />
-
# Last name : <%= f.text_field :last_name %><br />
-
# Biography : <%= f.text_area :biography %><br />
-
# Admin? : <%= f.check_box :admin %><br />
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# There, the argument is a symbol or string with the name of the
-
# object the form is about.
-
#
-
# The form builder acts as a regular form helper that somehow carries the
-
# model. Thus, the idea is that
-
#
-
# <%= f.text_field :first_name %>
-
#
-
# gets expanded to
-
#
-
# <%= text_field :person, :first_name %>
-
#
-
# The rightmost argument to +form_for+ is an
-
# optional hash of options:
-
#
-
# * <tt>:url</tt> - The URL the form is submitted to. It takes the same
-
# fields you pass to +url_for+ or +link_to+. In particular you may pass
-
# here a named route directly as well. Defaults to the current action.
-
# * <tt>:html</tt> - Optional HTML attributes for the form tag.
-
#
-
# Also note that +form_for+ doesn't create an exclusive scope. It's still
-
# possible to use both the stand-alone FormHelper methods and methods
-
# from FormTagHelper. For example:
-
#
-
# <%= form_for @person do |f| %>
-
# First name: <%= f.text_field :first_name %>
-
# Last name : <%= f.text_field :last_name %>
-
# Biography : <%= text_area :person, :biography %>
-
# Admin? : <%= check_box_tag "person[admin]", @person.company.admin? %>
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# This also works for the methods in FormOptionHelper and DateHelper that
-
# are designed to work with an object as base, like
-
# FormOptionHelper#collection_select and DateHelper#datetime_select.
-
#
-
# === Resource-oriented style
-
#
-
# As we said above, in addition to manually configuring the +form_for+
-
# call, you can rely on automated resource identification, which will use
-
# the conventions and named routes of that approach. This is the
-
# preferred way to use +form_for+ nowadays.
-
#
-
# For example, if <tt>@post</tt> is an existing record you want to edit
-
#
-
# <%= form_for @post do |f| %>
-
# ...
-
# <% end %>
-
#
-
# is equivalent to something like:
-
#
-
# <%= form_for @post, :as => :post, :url => post_path(@post), :method => :put, :html => { :class => "edit_post", :id => "edit_post_45" } do |f| %>
-
# ...
-
# <% end %>
-
#
-
# And for new records
-
#
-
# <%= form_for(Post.new) do |f| %>
-
# ...
-
# <% end %>
-
#
-
# is equivalent to something like:
-
#
-
# <%= form_for @post, :as => :post, :url => posts_path, :html => { :class => "new_post", :id => "new_post" } do |f| %>
-
# ...
-
# <% end %>
-
#
-
# You can also overwrite the individual conventions, like this:
-
#
-
# <%= form_for(@post, :url => super_posts_path) do |f| %>
-
# ...
-
# <% end %>
-
#
-
# You can also set the answer format, like this:
-
#
-
# <%= form_for(@post, :format => :json) do |f| %>
-
# ...
-
# <% end %>
-
#
-
# If you have an object that needs to be represented as a different
-
# parameter, like a Person that acts as a Client:
-
#
-
# <%= form_for(@person, :as => :client) do |f| %>
-
# ...
-
# <% end %>
-
#
-
# For namespaced routes, like +admin_post_url+:
-
#
-
# <%= form_for([:admin, @post]) do |f| %>
-
# ...
-
# <% end %>
-
#
-
# If your resource has associations defined, for example, you want to add comments
-
# to the document given that the routes are set correctly:
-
#
-
# <%= form_for([@document, @comment]) do |f| %>
-
# ...
-
# <% end %>
-
#
-
# Where <tt>@document = Document.find(params[:id])</tt> and
-
# <tt>@comment = Comment.new</tt>.
-
#
-
# === Setting the method
-
#
-
# You can force the form to use the full array of HTTP verbs by setting
-
#
-
# :method => (:get|:post|:put|:delete)
-
#
-
# in the options hash. If the verb is not GET or POST, which are natively supported by HTML forms, the
-
# form will be set to POST and a hidden input called _method will carry the intended verb for the server
-
# to interpret.
-
#
-
# === Unobtrusive JavaScript
-
#
-
# Specifying:
-
#
-
# :remote => true
-
#
-
# in the options hash creates a form that will allow the unobtrusive JavaScript drivers to modify its
-
# behavior. The expected default behavior is an XMLHttpRequest in the background instead of the regular
-
# POST arrangement, but ultimately the behavior is the choice of the JavaScript driver implementor.
-
# Even though it's using JavaScript to serialize the form elements, the form submission will work just like
-
# a regular submission as viewed by the receiving side (all elements available in <tt>params</tt>).
-
#
-
# Example:
-
#
-
# <%= form_for(@post, :remote => true) do |f| %>
-
# ...
-
# <% end %>
-
#
-
# The HTML generated for this would be:
-
#
-
# <form action='http://www.example.com' method='post' data-remote='true'>
-
# <div style='margin:0;padding:0;display:inline'>
-
# <input name='_method' type='hidden' value='put' />
-
# </div>
-
# ...
-
# </form>
-
#
-
# === Removing hidden model id's
-
#
-
# The form_for method automatically includes the model id as a hidden field in the form.
-
# This is used to maintain the correlation between the form data and its associated model.
-
# Some ORM systems do not use IDs on nested models so in this case you want to be able
-
# to disable the hidden id.
-
#
-
# In the following example the Post model has many Comments stored within it in a NoSQL database,
-
# thus there is no primary key for comments.
-
#
-
# Example:
-
#
-
# <%= form_for(@post) do |f| %>
-
# <% f.fields_for(:comments, :include_id => false) do |cf| %>
-
# ...
-
# <% end %>
-
# <% end %>
-
#
-
# === Customized form builders
-
#
-
# You can also build forms using a customized FormBuilder class. Subclass
-
# FormBuilder and override or define some more helpers, then use your
-
# custom builder. For example, let's say you made a helper to
-
# automatically add labels to form inputs.
-
#
-
# <%= form_for @person, :url => { :action => "create" }, :builder => LabellingFormBuilder do |f| %>
-
# <%= f.text_field :first_name %>
-
# <%= f.text_field :last_name %>
-
# <%= f.text_area :biography %>
-
# <%= f.check_box :admin %>
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# In this case, if you use this:
-
#
-
# <%= render f %>
-
#
-
# The rendered template is <tt>people/_labelling_form</tt> and the local
-
# variable referencing the form builder is called
-
# <tt>labelling_form</tt>.
-
#
-
# The custom FormBuilder class is automatically merged with the options
-
# of a nested fields_for call, unless it's explicitly set.
-
#
-
# In many cases you will want to wrap the above in another helper, so you
-
# could do something like the following:
-
#
-
# def labelled_form_for(record_or_name_or_array, *args, &proc)
-
# options = args.extract_options!
-
# form_for(record_or_name_or_array, *(args << options.merge(:builder => LabellingFormBuilder)), &proc)
-
# end
-
#
-
# If you don't need to attach a form to a model instance, then check out
-
# FormTagHelper#form_tag.
-
#
-
# === Form to external resources
-
#
-
# When you build forms to external resources sometimes you need to set an authenticity token or just render a form
-
# without it, for example when you submit data to a payment gateway number and types of fields could be limited.
-
#
-
# To set an authenticity token you need to pass an <tt>:authenticity_token</tt> parameter
-
#
-
# <%= form_for @invoice, :url => external_url, :authenticity_token => 'external_token' do |f|
-
# ...
-
# <% end %>
-
#
-
# If you don't want to an authenticity token field be rendered at all just pass <tt>false</tt>:
-
#
-
# <%= form_for @invoice, :url => external_url, :authenticity_token => false do |f|
-
# ...
-
# <% end %>
-
1
def form_for(record, options = {}, &proc)
-
raise ArgumentError, "Missing block" unless block_given?
-
-
options[:html] ||= {}
-
-
case record
-
when String, Symbol
-
object_name = record
-
object = nil
-
else
-
object = record.is_a?(Array) ? record.last : record
-
object_name = options[:as] || ActiveModel::Naming.param_key(object)
-
apply_form_for_options!(record, options)
-
end
-
-
options[:html][:remote] = options.delete(:remote) if options.has_key?(:remote)
-
options[:html][:method] = options.delete(:method) if options.has_key?(:method)
-
options[:html][:authenticity_token] = options.delete(:authenticity_token)
-
-
builder = options[:parent_builder] = instantiate_builder(object_name, object, options, &proc)
-
fields_for = fields_for(object_name, object, options, &proc)
-
default_options = builder.multipart? ? { :multipart => true } : {}
-
output = form_tag(options.delete(:url) || {}, default_options.merge!(options.delete(:html)))
-
output << fields_for
-
output.safe_concat('</form>')
-
end
-
-
1
def apply_form_for_options!(object_or_array, options) #:nodoc:
-
object = object_or_array.is_a?(Array) ? object_or_array.last : object_or_array
-
object = convert_to_model(object)
-
-
as = options[:as]
-
action, method = object.respond_to?(:persisted?) && object.persisted? ? [:edit, :put] : [:new, :post]
-
options[:html].reverse_merge!(
-
:class => as ? "#{as}_#{action}" : dom_class(object, action),
-
:id => as ? "#{as}_#{action}" : dom_id(object, action),
-
:method => method
-
)
-
-
options[:url] ||= polymorphic_path(object_or_array, :format => options.delete(:format))
-
end
-
1
private :apply_form_for_options!
-
-
# Creates a scope around a specific model object like form_for, but
-
# doesn't create the form tags themselves. This makes fields_for suitable
-
# for specifying additional model objects in the same form.
-
#
-
# === Generic Examples
-
#
-
# <%= form_for @person do |person_form| %>
-
# First name: <%= person_form.text_field :first_name %>
-
# Last name : <%= person_form.text_field :last_name %>
-
#
-
# <%= fields_for @person.permission do |permission_fields| %>
-
# Admin? : <%= permission_fields.check_box :admin %>
-
# <% end %>
-
#
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# ...or if you have an object that needs to be represented as a different
-
# parameter, like a Client that acts as a Person:
-
#
-
# <%= fields_for :person, @client do |permission_fields| %>
-
# Admin?: <%= permission_fields.check_box :admin %>
-
# <% end %>
-
#
-
# ...or if you don't have an object, just a name of the parameter:
-
#
-
# <%= fields_for :person do |permission_fields| %>
-
# Admin?: <%= permission_fields.check_box :admin %>
-
# <% end %>
-
#
-
# Note: This also works for the methods in FormOptionHelper and
-
# DateHelper that are designed to work with an object as base, like
-
# FormOptionHelper#collection_select and DateHelper#datetime_select.
-
#
-
# === Nested Attributes Examples
-
#
-
# When the object belonging to the current scope has a nested attribute
-
# writer for a certain attribute, fields_for will yield a new scope
-
# for that attribute. This allows you to create forms that set or change
-
# the attributes of a parent object and its associations in one go.
-
#
-
# Nested attribute writers are normal setter methods named after an
-
# association. The most common way of defining these writers is either
-
# with +accepts_nested_attributes_for+ in a model definition or by
-
# defining a method with the proper name. For example: the attribute
-
# writer for the association <tt>:address</tt> is called
-
# <tt>address_attributes=</tt>.
-
#
-
# Whether a one-to-one or one-to-many style form builder will be yielded
-
# depends on whether the normal reader method returns a _single_ object
-
# or an _array_ of objects.
-
#
-
# ==== One-to-one
-
#
-
# Consider a Person class which returns a _single_ Address from the
-
# <tt>address</tt> reader method and responds to the
-
# <tt>address_attributes=</tt> writer method:
-
#
-
# class Person
-
# def address
-
# @address
-
# end
-
#
-
# def address_attributes=(attributes)
-
# # Process the attributes hash
-
# end
-
# end
-
#
-
# This model can now be used with a nested fields_for, like so:
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <%= person_form.fields_for :address do |address_fields| %>
-
# Street : <%= address_fields.text_field :street %>
-
# Zip code: <%= address_fields.text_field :zip_code %>
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# When address is already an association on a Person you can use
-
# +accepts_nested_attributes_for+ to define the writer method for you:
-
#
-
# class Person < ActiveRecord::Base
-
# has_one :address
-
# accepts_nested_attributes_for :address
-
# end
-
#
-
# If you want to destroy the associated model through the form, you have
-
# to enable it first using the <tt>:allow_destroy</tt> option for
-
# +accepts_nested_attributes_for+:
-
#
-
# class Person < ActiveRecord::Base
-
# has_one :address
-
# accepts_nested_attributes_for :address, :allow_destroy => true
-
# end
-
#
-
# Now, when you use a form element with the <tt>_destroy</tt> parameter,
-
# with a value that evaluates to +true+, you will destroy the associated
-
# model (eg. 1, '1', true, or 'true'):
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <%= person_form.fields_for :address do |address_fields| %>
-
# ...
-
# Delete: <%= address_fields.check_box :_destroy %>
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# ==== One-to-many
-
#
-
# Consider a Person class which returns an _array_ of Project instances
-
# from the <tt>projects</tt> reader method and responds to the
-
# <tt>projects_attributes=</tt> writer method:
-
#
-
# class Person
-
# def projects
-
# [@project1, @project2]
-
# end
-
#
-
# def projects_attributes=(attributes)
-
# # Process the attributes hash
-
# end
-
# end
-
#
-
# This model can now be used with a nested fields_for. The block given to
-
# the nested fields_for call will be repeated for each instance in the
-
# collection:
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <%= person_form.fields_for :projects do |project_fields| %>
-
# <% if project_fields.object.active? %>
-
# Name: <%= project_fields.text_field :name %>
-
# <% end %>
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# It's also possible to specify the instance to be used:
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <% @person.projects.each do |project| %>
-
# <% if project.active? %>
-
# <%= person_form.fields_for :projects, project do |project_fields| %>
-
# Name: <%= project_fields.text_field :name %>
-
# <% end %>
-
# <% end %>
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# Or a collection to be used:
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <%= person_form.fields_for :projects, @active_projects do |project_fields| %>
-
# Name: <%= project_fields.text_field :name %>
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# When projects is already an association on Person you can use
-
# +accepts_nested_attributes_for+ to define the writer method for you:
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :projects
-
# accepts_nested_attributes_for :projects
-
# end
-
#
-
# If you want to destroy any of the associated models through the
-
# form, you have to enable it first using the <tt>:allow_destroy</tt>
-
# option for +accepts_nested_attributes_for+:
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :projects
-
# accepts_nested_attributes_for :projects, :allow_destroy => true
-
# end
-
#
-
# This will allow you to specify which models to destroy in the
-
# attributes hash by adding a form element for the <tt>_destroy</tt>
-
# parameter with a value that evaluates to +true+
-
# (eg. 1, '1', true, or 'true'):
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <%= person_form.fields_for :projects do |project_fields| %>
-
# Delete: <%= project_fields.check_box :_destroy %>
-
# <% end %>
-
# ...
-
# <% end %>
-
1
def fields_for(record_name, record_object = nil, options = {}, &block)
-
builder = instantiate_builder(record_name, record_object, options, &block)
-
output = capture(builder, &block)
-
output.concat builder.hidden_field(:id) if output && options[:hidden_field_id] && !builder.emitted_hidden_id?
-
output
-
end
-
-
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
-
# is found in the current I18n locale (through helpers.label.<modelname>.<attribute>) or you specify it explicitly.
-
# Additional options on the label tag can be passed as a hash with +options+. These options will be tagged
-
# onto the HTML as an HTML element attribute as in the example shown, except for the <tt>:value</tt> option, which is designed to
-
# target labels for radio_button tags (where the value is used in the ID of the input tag).
-
#
-
# ==== Examples
-
# label(:post, :title)
-
# # => <label for="post_title">Title</label>
-
#
-
# You can localize your labels based on model and attribute names.
-
# For example you can define the following in your locale (e.g. en.yml)
-
#
-
# helpers:
-
# label:
-
# post:
-
# body: "Write your entire text here"
-
#
-
# Which then will result in
-
#
-
# label(:post, :body)
-
# # => <label for="post_body">Write your entire text here</label>
-
#
-
# Localization can also be based purely on the translation of the attribute-name
-
# (if you are using ActiveRecord):
-
#
-
# activerecord:
-
# attributes:
-
# post:
-
# cost: "Total cost"
-
#
-
# label(:post, :cost)
-
# # => <label for="post_cost">Total cost</label>
-
#
-
# label(:post, :title, "A short title")
-
# # => <label for="post_title">A short title</label>
-
#
-
# label(:post, :title, "A short title", :class => "title_label")
-
# # => <label for="post_title" class="title_label">A short title</label>
-
#
-
# label(:post, :privacy, "Public Post", :value => "public")
-
# # => <label for="post_privacy_public">Public Post</label>
-
#
-
# label(:post, :terms) do
-
# 'Accept <a href="/terms">Terms</a>.'
-
# end
-
1
def label(object_name, method, content_or_options = nil, options = nil, &block)
-
content_is_options = content_or_options.is_a?(Hash)
-
if content_is_options || block_given?
-
options = content_or_options if content_is_options
-
text = nil
-
else
-
text = content_or_options
-
end
-
-
options ||= {}
-
InstanceTag.new(object_name, method, self, options.delete(:object)).to_label_tag(text, options, &block)
-
end
-
-
# Returns an input tag of the "text" type tailored for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
-
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
-
# shown.
-
#
-
# ==== Examples
-
# text_field(:post, :title, :size => 20)
-
# # => <input type="text" id="post_title" name="post[title]" size="20" value="#{@post.title}" />
-
#
-
# text_field(:post, :title, :class => "create_input")
-
# # => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" class="create_input" />
-
#
-
# text_field(:session, :user, :onchange => "if $('session[user]').value == 'admin' { alert('Your login can not be admin!'); }")
-
# # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange = "if $('session[user]').value == 'admin' { alert('Your login can not be admin!'); }"/>
-
#
-
# text_field(:snippet, :code, :size => 20, :class => 'code_input')
-
# # => <input type="text" id="snippet_code" name="snippet[code]" size="20" value="#{@snippet.code}" class="code_input" />
-
#
-
1
def text_field(object_name, method, options = {})
-
InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("text", options)
-
end
-
-
# Returns an input tag of the "password" type tailored for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
-
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
-
# shown.
-
#
-
# ==== Examples
-
# password_field(:login, :pass, :size => 20)
-
# # => <input type="password" id="login_pass" name="login[pass]" size="20" />
-
#
-
# password_field(:account, :secret, :class => "form_input", :value => @account.secret)
-
# # => <input type="password" id="account_secret" name="account[secret]" value="#{@account.secret}" class="form_input" />
-
#
-
# password_field(:user, :password, :onchange => "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }")
-
# # => <input type="password" id="user_password" name="user[password]" onchange = "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }"/>
-
#
-
# password_field(:account, :pin, :size => 20, :class => 'form_input')
-
# # => <input type="password" id="account_pin" name="account[pin]" size="20" class="form_input" />
-
#
-
1
def password_field(object_name, method, options = {})
-
InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("password", { :value => nil }.merge!(options))
-
end
-
-
# Returns a hidden input tag tailored for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
-
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
-
# shown.
-
#
-
# ==== Examples
-
# hidden_field(:signup, :pass_confirm)
-
# # => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="#{@signup.pass_confirm}" />
-
#
-
# hidden_field(:post, :tag_list)
-
# # => <input type="hidden" id="post_tag_list" name="post[tag_list]" value="#{@post.tag_list}" />
-
#
-
# hidden_field(:user, :token)
-
# # => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
-
1
def hidden_field(object_name, method, options = {})
-
InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("hidden", options)
-
end
-
-
# Returns a file upload input tag tailored for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
-
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
-
# shown.
-
#
-
# Using this method inside a +form_for+ block will set the enclosing form's encoding to <tt>multipart/form-data</tt>.
-
#
-
# ==== Examples
-
# file_field(:user, :avatar)
-
# # => <input type="file" id="user_avatar" name="user[avatar]" />
-
#
-
# file_field(:post, :attached, :accept => 'text/html')
-
# # => <input type="file" id="post_attached" name="post[attached]" />
-
#
-
# file_field(:attachment, :file, :class => 'file_input')
-
# # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
-
#
-
1
def file_field(object_name, method, options = {})
-
InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("file", options.update({:size => nil}))
-
end
-
-
# Returns a textarea opening and closing tag set tailored for accessing a specified attribute (identified by +method+)
-
# on an object assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
-
# hash with +options+.
-
#
-
# ==== Examples
-
# text_area(:post, :body, :cols => 20, :rows => 40)
-
# # => <textarea cols="20" rows="40" id="post_body" name="post[body]">
-
# # #{@post.body}
-
# # </textarea>
-
#
-
# text_area(:comment, :text, :size => "20x30")
-
# # => <textarea cols="20" rows="30" id="comment_text" name="comment[text]">
-
# # #{@comment.text}
-
# # </textarea>
-
#
-
# text_area(:application, :notes, :cols => 40, :rows => 15, :class => 'app_input')
-
# # => <textarea cols="40" rows="15" id="application_notes" name="application[notes]" class="app_input">
-
# # #{@application.notes}
-
# # </textarea>
-
#
-
# text_area(:entry, :body, :size => "20x20", :disabled => 'disabled')
-
# # => <textarea cols="20" rows="20" id="entry_body" name="entry[body]" disabled="disabled">
-
# # #{@entry.body}
-
# # </textarea>
-
1
def text_area(object_name, method, options = {})
-
InstanceTag.new(object_name, method, self, options.delete(:object)).to_text_area_tag(options)
-
end
-
-
# Returns a checkbox tag tailored for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). This object must be an instance object (@object) and not a local object.
-
# It's intended that +method+ returns an integer and if that integer is above zero, then the checkbox is checked.
-
# Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1
-
# while the default +unchecked_value+ is set to 0 which is convenient for boolean values.
-
#
-
# ==== Gotcha
-
#
-
# The HTML specification says unchecked check boxes are not successful, and
-
# thus web browsers do not send them. Unfortunately this introduces a gotcha:
-
# if an +Invoice+ model has a +paid+ flag, and in the form that edits a paid
-
# invoice the user unchecks its check box, no +paid+ parameter is sent. So,
-
# any mass-assignment idiom like
-
#
-
# @invoice.update_attributes(params[:invoice])
-
#
-
# wouldn't update the flag.
-
#
-
# To prevent this the helper generates an auxiliary hidden field before
-
# the very check box. The hidden field has the same name and its
-
# attributes mimic an unchecked check box.
-
#
-
# This way, the client either sends only the hidden field (representing
-
# the check box is unchecked), or both fields. Since the HTML specification
-
# says key/value pairs have to be sent in the same order they appear in the
-
# form, and parameters extraction gets the last occurrence of any repeated
-
# key in the query string, that works for ordinary forms.
-
#
-
# Unfortunately that workaround does not work when the check box goes
-
# within an array-like parameter, as in
-
#
-
# <%= fields_for "project[invoice_attributes][]", invoice, :index => nil do |form| %>
-
# <%= form.check_box :paid %>
-
# ...
-
# <% end %>
-
#
-
# because parameter name repetition is precisely what Rails seeks to distinguish
-
# the elements of the array. For each item with a checked check box you
-
# get an extra ghost item with only that attribute, assigned to "0".
-
#
-
# In that case it is preferable to either use +check_box_tag+ or to use
-
# hashes instead of arrays.
-
#
-
# ==== Examples
-
# # Let's say that @post.validated? is 1:
-
# check_box("post", "validated")
-
# # => <input name="post[validated]" type="hidden" value="0" />
-
# # <input type="checkbox" id="post_validated" name="post[validated]" value="1" />
-
#
-
# # Let's say that @puppy.gooddog is "no":
-
# check_box("puppy", "gooddog", {}, "yes", "no")
-
# # => <input name="puppy[gooddog]" type="hidden" value="no" />
-
# # <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
-
#
-
# check_box("eula", "accepted", { :class => 'eula_check' }, "yes", "no")
-
# # => <input name="eula[accepted]" type="hidden" value="no" />
-
# # <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
-
#
-
1
def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
-
InstanceTag.new(object_name, method, self, options.delete(:object)).to_check_box_tag(options, checked_value, unchecked_value)
-
end
-
-
# Returns a radio button tag for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). If the current value of +method+ is +tag_value+ the
-
# radio button will be checked.
-
#
-
# To force the radio button to be checked pass <tt>:checked => true</tt> in the
-
# +options+ hash. You may pass HTML options there as well.
-
#
-
# ==== Examples
-
# # Let's say that @post.category returns "rails":
-
# radio_button("post", "category", "rails")
-
# radio_button("post", "category", "java")
-
# # => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
-
# # <input type="radio" id="post_category_java" name="post[category]" value="java" />
-
#
-
# radio_button("user", "receive_newsletter", "yes")
-
# radio_button("user", "receive_newsletter", "no")
-
# # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
-
# # <input type="radio" id="user_receive_newsletter_no" name="user[receive_newsletter]" value="no" checked="checked" />
-
1
def radio_button(object_name, method, tag_value, options = {})
-
InstanceTag.new(object_name, method, self, options.delete(:object)).to_radio_button_tag(tag_value, options)
-
end
-
-
# Returns an input of type "search" for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). Inputs of type "search" may be styled differently by
-
# some browsers
-
#
-
# ==== Examples
-
#
-
# search_field(:user, :name)
-
# # => <input id="user_name" name="user[name]" size="30" type="search" />
-
# search_field(:user, :name, :autosave => false)
-
# # => <input autosave="false" id="user_name" name="user[name]" size="30" type="search" />
-
# search_field(:user, :name, :results => 3)
-
# # => <input id="user_name" name="user[name]" results="3" size="30" type="search" />
-
# # Assume request.host returns "www.example.com"
-
# search_field(:user, :name, :autosave => true)
-
# # => <input autosave="com.example.www" id="user_name" name="user[name]" results="10" size="30" type="search" />
-
# search_field(:user, :name, :onsearch => true)
-
# # => <input id="user_name" incremental="true" name="user[name]" onsearch="true" size="30" type="search" />
-
# search_field(:user, :name, :autosave => false, :onsearch => true)
-
# # => <input autosave="false" id="user_name" incremental="true" name="user[name]" onsearch="true" size="30" type="search" />
-
# search_field(:user, :name, :autosave => true, :onsearch => true)
-
# # => <input autosave="com.example.www" id="user_name" incremental="true" name="user[name]" onsearch="true" results="10" size="30" type="search" />
-
-
1
def search_field(object_name, method, options = {})
-
options = options.stringify_keys
-
-
if options["autosave"]
-
if options["autosave"] == true
-
options["autosave"] = request.host.split(".").reverse.join(".")
-
end
-
options["results"] ||= 10
-
end
-
-
if options["onsearch"]
-
options["incremental"] = true unless options.has_key?("incremental")
-
end
-
-
InstanceTag.new(object_name, method, self, options.delete("object")).to_input_field_tag("search", options)
-
end
-
-
# Returns a text_field of type "tel".
-
#
-
# telephone_field("user", "phone")
-
# # => <input id="user_phone" name="user[phone]" size="30" type="tel" />
-
-
1
def telephone_field(object_name, method, options = {})
-
InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("tel", options)
-
end
-
1
alias phone_field telephone_field
-
-
# Returns a text_field of type "url".
-
#
-
# url_field("user", "homepage")
-
# # => <input id="user_homepage" size="30" name="user[homepage]" type="url" />
-
-
1
def url_field(object_name, method, options = {})
-
InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("url", options)
-
end
-
-
# Returns a text_field of type "email".
-
#
-
# email_field("user", "address")
-
# # => <input id="user_address" size="30" name="user[address]" type="email" />
-
-
1
def email_field(object_name, method, options = {})
-
InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("email", options)
-
end
-
-
# Returns an input tag of type "number".
-
#
-
# ==== Options
-
# * Accepts same options as number_field_tag
-
1
def number_field(object_name, method, options = {})
-
InstanceTag.new(object_name, method, self, options.delete(:object)).to_number_field_tag("number", options)
-
end
-
-
# Returns an input tag of type "range".
-
#
-
# ==== Options
-
# * Accepts same options as range_field_tag
-
1
def range_field(object_name, method, options = {})
-
InstanceTag.new(object_name, method, self, options.delete(:object)).to_number_field_tag("range", options)
-
end
-
-
1
private
-
-
1
def instantiate_builder(record_name, record_object, options, &block)
-
case record_name
-
when String, Symbol
-
object = record_object
-
object_name = record_name
-
else
-
object = record_name
-
object_name = ActiveModel::Naming.param_key(object)
-
end
-
-
builder = options[:builder] || ActionView::Base.default_form_builder
-
builder.new(object_name, object, self, options, block)
-
end
-
end
-
-
1
class InstanceTag
-
1
include Helpers::CaptureHelper, Context, Helpers::TagHelper, Helpers::FormTagHelper
-
-
1
attr_reader :object, :method_name, :object_name
-
-
1
DEFAULT_FIELD_OPTIONS = { "size" => 30 }
-
1
DEFAULT_RADIO_OPTIONS = { }
-
1
DEFAULT_TEXT_AREA_OPTIONS = { "cols" => 40, "rows" => 20 }
-
-
1
def initialize(object_name, method_name, template_object, object = nil)
-
@object_name, @method_name = object_name.to_s.dup, method_name.to_s.dup
-
@template_object = template_object
-
@object_name.sub!(/\[\]$/,"") || @object_name.sub!(/\[\]\]$/,"]")
-
@object = retrieve_object(object)
-
@auto_index = retrieve_autoindex(Regexp.last_match.pre_match) if Regexp.last_match
-
end
-
-
1
def to_label_tag(text = nil, options = {}, &block)
-
options = options.stringify_keys
-
tag_value = options.delete("value")
-
name_and_id = options.dup
-
-
if name_and_id["for"]
-
name_and_id["id"] = name_and_id["for"]
-
else
-
name_and_id.delete("id")
-
end
-
-
add_default_name_and_id_for_value(tag_value, name_and_id)
-
options.delete("index")
-
options["for"] ||= name_and_id["id"]
-
-
if block_given?
-
label_tag(name_and_id["id"], options, &block)
-
else
-
content = if text.blank?
-
method_and_value = tag_value.present? ? "#{method_name}.#{tag_value}" : method_name
-
I18n.t("helpers.label.#{object_name}.#{method_and_value}", :default => "").presence
-
else
-
text.to_s
-
end
-
-
content ||= if object && object.class.respond_to?(:human_attribute_name)
-
object.class.human_attribute_name(method_name)
-
end
-
-
content ||= method_name.humanize
-
-
label_tag(name_and_id["id"], content, options)
-
end
-
end
-
-
1
def to_input_field_tag(field_type, options = {})
-
options = options.stringify_keys
-
options["size"] = options["maxlength"] || DEFAULT_FIELD_OPTIONS["size"] unless options.key?("size")
-
options = DEFAULT_FIELD_OPTIONS.merge(options)
-
if field_type == "hidden"
-
options.delete("size")
-
end
-
options["type"] ||= field_type
-
options["value"] = options.fetch("value"){ value_before_type_cast(object) } unless field_type == "file"
-
options["value"] &&= ERB::Util.html_escape(options["value"])
-
add_default_name_and_id(options)
-
tag("input", options)
-
end
-
-
1
def to_number_field_tag(field_type, options = {})
-
options = options.stringify_keys
-
if range = options.delete("in") || options.delete("within")
-
options.update("min" => range.min, "max" => range.max)
-
end
-
to_input_field_tag(field_type, options)
-
end
-
-
1
def to_radio_button_tag(tag_value, options = {})
-
options = DEFAULT_RADIO_OPTIONS.merge(options.stringify_keys)
-
options["type"] = "radio"
-
options["value"] = tag_value
-
if options.has_key?("checked")
-
cv = options.delete "checked"
-
checked = cv == true || cv == "checked"
-
else
-
checked = self.class.radio_button_checked?(value(object), tag_value)
-
end
-
options["checked"] = "checked" if checked
-
add_default_name_and_id_for_value(tag_value, options)
-
tag("input", options)
-
end
-
-
1
def to_text_area_tag(options = {})
-
options = DEFAULT_TEXT_AREA_OPTIONS.merge(options.stringify_keys)
-
add_default_name_and_id(options)
-
-
if size = options.delete("size")
-
options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
-
end
-
-
content_tag("textarea", ERB::Util.html_escape(options.delete('value') || value_before_type_cast(object)), options)
-
end
-
-
1
def to_check_box_tag(options = {}, checked_value = "1", unchecked_value = "0")
-
options = options.stringify_keys
-
options["type"] = "checkbox"
-
options["value"] = checked_value
-
if options.has_key?("checked")
-
cv = options.delete "checked"
-
checked = cv == true || cv == "checked"
-
else
-
checked = self.class.check_box_checked?(value(object), checked_value)
-
end
-
options["checked"] = "checked" if checked
-
if options["multiple"]
-
add_default_name_and_id_for_value(checked_value, options)
-
options.delete("multiple")
-
else
-
add_default_name_and_id(options)
-
end
-
hidden = tag("input", "name" => options["name"], "type" => "hidden", "value" => options['disabled'] && checked ? checked_value : unchecked_value)
-
checkbox = tag("input", options)
-
(hidden + checkbox).html_safe
-
end
-
-
1
def to_boolean_select_tag(options = {})
-
options = options.stringify_keys
-
add_default_name_and_id(options)
-
value = value(object)
-
tag_text = "<select"
-
tag_text << tag_options(options)
-
tag_text << "><option value=\"false\""
-
tag_text << " selected" if value == false
-
tag_text << ">False</option><option value=\"true\""
-
tag_text << " selected" if value
-
tag_text << ">True</option></select>"
-
end
-
-
1
def to_content_tag(tag_name, options = {})
-
content_tag(tag_name, value(object), options)
-
end
-
-
1
def retrieve_object(object)
-
if object
-
object
-
elsif @template_object.instance_variable_defined?("@#{@object_name}")
-
@template_object.instance_variable_get("@#{@object_name}")
-
end
-
rescue NameError
-
# As @object_name may contain the nested syntax (item[subobject]) we need to fallback to nil.
-
nil
-
end
-
-
1
def retrieve_autoindex(pre_match)
-
object = self.object || @template_object.instance_variable_get("@#{pre_match}")
-
if object && object.respond_to?(:to_param)
-
object.to_param
-
else
-
raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
-
end
-
end
-
-
1
def value(object)
-
self.class.value(object, @method_name)
-
end
-
-
1
def value_before_type_cast(object)
-
self.class.value_before_type_cast(object, @method_name)
-
end
-
-
1
class << self
-
1
def value(object, method_name)
-
object.send method_name if object
-
end
-
-
1
def value_before_type_cast(object, method_name)
-
unless object.nil?
-
object.respond_to?(method_name + "_before_type_cast") ?
-
object.send(method_name + "_before_type_cast") :
-
object.send(method_name)
-
end
-
end
-
-
1
def check_box_checked?(value, checked_value)
-
case value
-
when TrueClass, FalseClass
-
value
-
when NilClass
-
false
-
when Integer
-
value != 0
-
when String
-
value == checked_value
-
when Array
-
value.include?(checked_value)
-
else
-
value.to_i != 0
-
end
-
end
-
-
1
def radio_button_checked?(value, checked_value)
-
value.to_s == checked_value.to_s
-
end
-
end
-
-
1
private
-
1
def add_default_name_and_id_for_value(tag_value, options)
-
unless tag_value.nil?
-
pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/[^-\w]/, "").downcase
-
specified_id = options["id"]
-
add_default_name_and_id(options)
-
options["id"] += "_#{pretty_tag_value}" if specified_id.blank? && options["id"].present?
-
else
-
add_default_name_and_id(options)
-
end
-
end
-
-
1
def add_default_name_and_id(options)
-
if options.has_key?("index")
-
options["name"] ||= tag_name_with_index(options["index"])
-
options["id"] = options.fetch("id"){ tag_id_with_index(options["index"]) }
-
options.delete("index")
-
elsif defined?(@auto_index)
-
options["name"] ||= tag_name_with_index(@auto_index)
-
options["id"] = options.fetch("id"){ tag_id_with_index(@auto_index) }
-
else
-
options["name"] ||= tag_name + (options['multiple'] ? '[]' : '')
-
options["id"] = options.fetch("id"){ tag_id }
-
end
-
end
-
-
1
def tag_name
-
"#{@object_name}[#{sanitized_method_name}]"
-
end
-
-
1
def tag_name_with_index(index)
-
"#{@object_name}[#{index}][#{sanitized_method_name}]"
-
end
-
-
1
def tag_id
-
"#{sanitized_object_name}_#{sanitized_method_name}"
-
end
-
-
1
def tag_id_with_index(index)
-
"#{sanitized_object_name}_#{index}_#{sanitized_method_name}"
-
end
-
-
1
def sanitized_object_name
-
@sanitized_object_name ||= @object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
-
end
-
-
1
def sanitized_method_name
-
@sanitized_method_name ||= @method_name.sub(/\?$/,"")
-
end
-
end
-
-
1
class FormBuilder
-
# The methods which wrap a form helper call.
-
1
class_attribute :field_helpers
-
1
self.field_helpers = FormHelper.instance_method_names - %w(form_for convert_to_model)
-
-
1
attr_accessor :object_name, :object, :options
-
-
1
attr_reader :multipart, :parent_builder
-
1
alias :multipart? :multipart
-
-
1
def multipart=(multipart)
-
@multipart = multipart
-
parent_builder.multipart = multipart if parent_builder
-
end
-
-
1
def self.model_name
-
@model_name ||= Struct.new(:partial_path).new(name.demodulize.underscore.sub!(/_builder$/, ''))
-
end
-
-
1
def to_model
-
self
-
end
-
-
1
def initialize(object_name, object, template, options, proc)
-
@nested_child_index = {}
-
@object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
-
@parent_builder = options[:parent_builder]
-
@default_options = @options ? @options.slice(:index) : {}
-
if @object_name.to_s.match(/\[\]$/)
-
if object ||= @template.instance_variable_get("@#{Regexp.last_match.pre_match}") and object.respond_to?(:to_param)
-
@auto_index = object.to_param
-
else
-
raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
-
end
-
end
-
@multipart = nil
-
end
-
-
1
(field_helpers - %w(label check_box radio_button fields_for hidden_field file_field)).each do |selector|
-
10
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
def #{selector}(method, options = {}) # def text_field(method, options = {})
-
@template.send( # @template.send(
-
#{selector.inspect}, # "text_field",
-
@object_name, # @object_name,
-
method, # method,
-
objectify_options(options)) # objectify_options(options))
-
end # end
-
RUBY_EVAL
-
end
-
-
1
def fields_for(record_name, record_object = nil, fields_options = {}, &block)
-
fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options?
-
fields_options[:builder] ||= options[:builder]
-
fields_options[:parent_builder] = self
-
-
case record_name
-
when String, Symbol
-
if nested_attributes_association?(record_name)
-
return fields_for_with_nested_attributes(record_name, record_object, fields_options, block)
-
end
-
else
-
record_object = record_name.is_a?(Array) ? record_name.last : record_name
-
record_name = ActiveModel::Naming.param_key(record_object)
-
end
-
-
index = if options.has_key?(:index)
-
"[#{options[:index]}]"
-
elsif defined?(@auto_index)
-
self.object_name = @object_name.to_s.sub(/\[\]$/,"")
-
"[#{@auto_index}]"
-
end
-
record_name = "#{object_name}#{index}[#{record_name}]"
-
-
@template.fields_for(record_name, record_object, fields_options, &block)
-
end
-
-
1
def label(method, text = nil, options = {}, &block)
-
@template.label(@object_name, method, text, objectify_options(options), &block)
-
end
-
-
1
def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
-
@template.check_box(@object_name, method, objectify_options(options), checked_value, unchecked_value)
-
end
-
-
1
def radio_button(method, tag_value, options = {})
-
@template.radio_button(@object_name, method, tag_value, objectify_options(options))
-
end
-
-
1
def hidden_field(method, options = {})
-
@emitted_hidden_id = true if method == :id
-
@template.hidden_field(@object_name, method, objectify_options(options))
-
end
-
-
1
def file_field(method, options = {})
-
self.multipart = true
-
@template.file_field(@object_name, method, objectify_options(options))
-
end
-
-
# Add the submit button for the given form. When no value is given, it checks
-
# if the object is a new resource or not to create the proper label:
-
#
-
# <%= form_for @post do |f| %>
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# In the example above, if @post is a new record, it will use "Create Post" as
-
# submit button label, otherwise, it uses "Update Post".
-
#
-
# Those labels can be customized using I18n, under the helpers.submit key and accept
-
# the %{model} as translation interpolation:
-
#
-
# en:
-
# helpers:
-
# submit:
-
# create: "Create a %{model}"
-
# update: "Confirm changes to %{model}"
-
#
-
# It also searches for a key specific for the given object:
-
#
-
# en:
-
# helpers:
-
# submit:
-
# post:
-
# create: "Add %{model}"
-
#
-
1
def submit(value=nil, options={})
-
value, options = nil, value if value.is_a?(Hash)
-
value ||= submit_default_value
-
@template.submit_tag(value, options)
-
end
-
-
1
def emitted_hidden_id?
-
@emitted_hidden_id ||= nil
-
end
-
-
1
private
-
1
def objectify_options(options)
-
@default_options.merge(options.merge(:object => @object))
-
end
-
-
1
def submit_default_value
-
object = convert_to_model(@object)
-
key = object ? (object.persisted? ? :update : :create) : :submit
-
-
model = if object.class.respond_to?(:model_name)
-
object.class.model_name.human
-
else
-
@object_name.to_s.humanize
-
end
-
-
defaults = []
-
defaults << :"helpers.submit.#{object_name}.#{key}"
-
defaults << :"helpers.submit.#{key}"
-
defaults << "#{key.to_s.humanize} #{model}"
-
-
I18n.t(defaults.shift, :model => model, :default => defaults)
-
end
-
-
1
def nested_attributes_association?(association_name)
-
@object.respond_to?("#{association_name}_attributes=")
-
end
-
-
1
def fields_for_with_nested_attributes(association_name, association, options, block)
-
name = "#{object_name}[#{association_name}_attributes]"
-
association = convert_to_model(association)
-
-
if association.respond_to?(:persisted?)
-
association = [association] if @object.send(association_name).is_a?(Array)
-
elsif !association.respond_to?(:to_ary)
-
association = @object.send(association_name)
-
end
-
-
if association.respond_to?(:to_ary)
-
explicit_child_index = options[:child_index]
-
output = ActiveSupport::SafeBuffer.new
-
association.each do |child|
-
output << fields_for_nested_model("#{name}[#{explicit_child_index || nested_child_index(name)}]", child, options, block)
-
end
-
output
-
elsif association
-
fields_for_nested_model(name, association, options, block)
-
end
-
end
-
-
1
def fields_for_nested_model(name, object, options, block)
-
object = convert_to_model(object)
-
-
parent_include_id = self.options.fetch(:include_id, true)
-
include_id = options.fetch(:include_id, parent_include_id)
-
options[:hidden_field_id] = object.persisted? && include_id
-
@template.fields_for(name, object, options, &block)
-
end
-
-
1
def nested_child_index(name)
-
@nested_child_index[name] ||= -1
-
@nested_child_index[name] += 1
-
end
-
-
1
def convert_to_model(object)
-
object.respond_to?(:to_model) ? object.to_model : object
-
end
-
end
-
end
-
-
1
ActiveSupport.on_load(:action_view) do
-
1
class ActionView::Base
-
1
cattr_accessor :default_form_builder
-
1
@@default_form_builder = ::ActionView::Helpers::FormBuilder
-
end
-
end
-
end
-
1
require 'cgi'
-
1
require 'erb'
-
1
require 'action_view/helpers/form_helper'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/string/output_safety'
-
-
1
module ActionView
-
# = Action View Form Option Helpers
-
1
module Helpers
-
# Provides a number of methods for turning different kinds of containers into a set of option tags.
-
# == Options
-
# The <tt>collection_select</tt>, <tt>select</tt> and <tt>time_zone_select</tt> methods take an <tt>options</tt> parameter, a hash:
-
#
-
# * <tt>:include_blank</tt> - set to true or a prompt string if the first option element of the select element is a blank. Useful if there is not a default value required for the select element.
-
#
-
# For example,
-
#
-
# select("post", "category", Post::CATEGORIES, {:include_blank => true})
-
#
-
# could become:
-
#
-
# <select name="post[category]">
-
# <option></option>
-
# <option>joke</option>
-
# <option>poem</option>
-
# </select>
-
#
-
# Another common case is a select tag for an <tt>belongs_to</tt>-associated object.
-
#
-
# Example with @post.person_id => 2:
-
#
-
# select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {:include_blank => 'None'})
-
#
-
# could become:
-
#
-
# <select name="post[person_id]">
-
# <option value="">None</option>
-
# <option value="1">David</option>
-
# <option value="2" selected="selected">Sam</option>
-
# <option value="3">Tobias</option>
-
# </select>
-
#
-
# * <tt>:prompt</tt> - set to true or a prompt string. When the select element doesn't have a value yet, this prepends an option with a generic prompt -- "Please select" -- or the given prompt string.
-
#
-
# Example:
-
#
-
# select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {:prompt => 'Select Person'})
-
#
-
# could become:
-
#
-
# <select name="post[person_id]">
-
# <option value="">Select Person</option>
-
# <option value="1">David</option>
-
# <option value="2">Sam</option>
-
# <option value="3">Tobias</option>
-
# </select>
-
#
-
# Like the other form helpers, +select+ can accept an <tt>:index</tt> option to manually set the ID used in the resulting output. Unlike other helpers, +select+ expects this
-
# option to be in the +html_options+ parameter.
-
#
-
# Example:
-
#
-
# select("album[]", "genre", %w[rap rock country], {}, { :index => nil })
-
#
-
# becomes:
-
#
-
# <select name="album[][genre]" id="album__genre">
-
# <option value="rap">rap</option>
-
# <option value="rock">rock</option>
-
# <option value="country">country</option>
-
# </select>
-
#
-
# * <tt>:disabled</tt> - can be a single value or an array of values that will be disabled options in the final output.
-
#
-
# Example:
-
#
-
# select("post", "category", Post::CATEGORIES, {:disabled => 'restricted'})
-
#
-
# could become:
-
#
-
# <select name="post[category]">
-
# <option></option>
-
# <option>joke</option>
-
# <option>poem</option>
-
# <option disabled="disabled">restricted</option>
-
# </select>
-
#
-
# When used with the <tt>collection_select</tt> helper, <tt>:disabled</tt> can also be a Proc that identifies those options that should be disabled.
-
#
-
# Example:
-
#
-
# collection_select(:post, :category_id, Category.all, :id, :name, {:disabled => lambda{|category| category.archived? }})
-
#
-
# If the categories "2008 stuff" and "Christmas" return true when the method <tt>archived?</tt> is called, this would return:
-
# <select name="post[category_id]">
-
# <option value="1" disabled="disabled">2008 stuff</option>
-
# <option value="2" disabled="disabled">Christmas</option>
-
# <option value="3">Jokes</option>
-
# <option value="4">Poems</option>
-
# </select>
-
#
-
1
module FormOptionsHelper
-
# ERB::Util can mask some helpers like textilize. Make sure to include them.
-
1
include TextHelper
-
-
# Create a select tag and a series of contained option tags for the provided object and method.
-
# The option currently held by the object will be selected, provided that the object is available.
-
# See options_for_select for the required format of the choices parameter.
-
#
-
# Example with @post.person_id => 1:
-
# select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, { :include_blank => true })
-
#
-
# could become:
-
#
-
# <select name="post[person_id]">
-
# <option value=""></option>
-
# <option value="1" selected="selected">David</option>
-
# <option value="2">Sam</option>
-
# <option value="3">Tobias</option>
-
# </select>
-
#
-
# This can be used to provide a default set of options in the standard way: before rendering the create form, a
-
# new model instance is assigned the default options and bound to @model_name. Usually this model is not saved
-
# to the database. Instead, a second model object is created when the create request is received.
-
# This allows the user to submit a form page more than once with the expected results of creating multiple records.
-
# In addition, this allows a single partial to be used to generate form inputs for both edit and create forms.
-
#
-
# By default, <tt>post.person_id</tt> is the selected option. Specify <tt>:selected => value</tt> to use a different selection
-
# or <tt>:selected => nil</tt> to leave all options unselected. Similarly, you can specify values to be disabled in the option
-
# tags by specifying the <tt>:disabled</tt> option. This can either be a single value or an array of values to be disabled.
-
1
def select(object, method, choices, options = {}, html_options = {})
-
InstanceTag.new(object, method, self, options.delete(:object)).to_select_tag(choices, options, html_options)
-
end
-
-
# Returns <tt><select></tt> and <tt><option></tt> tags for the collection of existing return values of
-
# +method+ for +object+'s class. The value returned from calling +method+ on the instance +object+ will
-
# be selected. If calling +method+ returns +nil+, no selection is made without including <tt>:prompt</tt>
-
# or <tt>:include_blank</tt> in the +options+ hash.
-
#
-
# The <tt>:value_method</tt> and <tt>:text_method</tt> parameters are methods to be called on each member
-
# of +collection+. The return values are used as the +value+ attribute and contents of each
-
# <tt><option></tt> tag, respectively.
-
#
-
# Example object structure for use with this method:
-
# class Post < ActiveRecord::Base
-
# belongs_to :author
-
# end
-
# class Author < ActiveRecord::Base
-
# has_many :posts
-
# def name_with_initial
-
# "#{first_name.first}. #{last_name}"
-
# end
-
# end
-
#
-
# Sample usage (selecting the associated Author for an instance of Post, <tt>@post</tt>):
-
# collection_select(:post, :author_id, Author.all, :id, :name_with_initial, :prompt => true)
-
#
-
# If <tt>@post.author_id</tt> is already <tt>1</tt>, this would return:
-
# <select name="post[author_id]">
-
# <option value="">Please select</option>
-
# <option value="1" selected="selected">D. Heinemeier Hansson</option>
-
# <option value="2">D. Thomas</option>
-
# <option value="3">M. Clark</option>
-
# </select>
-
1
def collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})
-
InstanceTag.new(object, method, self, options.delete(:object)).to_collection_select_tag(collection, value_method, text_method, options, html_options)
-
end
-
-
-
# Returns <tt><select></tt>, <tt><optgroup></tt> and <tt><option></tt> tags for the collection of existing return values of
-
# +method+ for +object+'s class. The value returned from calling +method+ on the instance +object+ will
-
# be selected. If calling +method+ returns +nil+, no selection is made without including <tt>:prompt</tt>
-
# or <tt>:include_blank</tt> in the +options+ hash.
-
#
-
# Parameters:
-
# * +object+ - The instance of the class to be used for the select tag
-
# * +method+ - The attribute of +object+ corresponding to the select tag
-
# * +collection+ - An array of objects representing the <tt><optgroup></tt> tags.
-
# * +group_method+ - The name of a method which, when called on a member of +collection+, returns an
-
# array of child objects representing the <tt><option></tt> tags.
-
# * +group_label_method+ - The name of a method which, when called on a member of +collection+, returns a
-
# string to be used as the +label+ attribute for its <tt><optgroup></tt> tag.
-
# * +option_key_method+ - The name of a method which, when called on a child object of a member of
-
# +collection+, returns a value to be used as the +value+ attribute for its <tt><option></tt> tag.
-
# * +option_value_method+ - The name of a method which, when called on a child object of a member of
-
# +collection+, returns a value to be used as the contents of its <tt><option></tt> tag.
-
#
-
# Example object structure for use with this method:
-
# class Continent < ActiveRecord::Base
-
# has_many :countries
-
# # attribs: id, name
-
# end
-
# class Country < ActiveRecord::Base
-
# belongs_to :continent
-
# # attribs: id, name, continent_id
-
# end
-
# class City < ActiveRecord::Base
-
# belongs_to :country
-
# # attribs: id, name, country_id
-
# end
-
#
-
# Sample usage:
-
# grouped_collection_select(:city, :country_id, @continents, :countries, :name, :id, :name)
-
#
-
# Possible output:
-
# <select name="city[country_id]">
-
# <optgroup label="Africa">
-
# <option value="1">South Africa</option>
-
# <option value="3">Somalia</option>
-
# </optgroup>
-
# <optgroup label="Europe">
-
# <option value="7" selected="selected">Denmark</option>
-
# <option value="2">Ireland</option>
-
# </optgroup>
-
# </select>
-
#
-
1
def grouped_collection_select(object, method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
-
InstanceTag.new(object, method, self, options.delete(:object)).to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
-
end
-
-
# Return select and option tags for the given object and method, using
-
# #time_zone_options_for_select to generate the list of option tags.
-
#
-
# In addition to the <tt>:include_blank</tt> option documented above,
-
# this method also supports a <tt>:model</tt> option, which defaults
-
# to ActiveSupport::TimeZone. This may be used by users to specify a
-
# different time zone model object. (See +time_zone_options_for_select+
-
# for more information.)
-
#
-
# You can also supply an array of ActiveSupport::TimeZone objects
-
# as +priority_zones+, so that they will be listed above the rest of the
-
# (long) list. (You can use ActiveSupport::TimeZone.us_zones as a convenience
-
# for obtaining a list of the US time zones, or a Regexp to select the zones
-
# of your choice)
-
#
-
# Finally, this method supports a <tt>:default</tt> option, which selects
-
# a default ActiveSupport::TimeZone if the object's time zone is +nil+.
-
#
-
# Examples:
-
# time_zone_select( "user", "time_zone", nil, :include_blank => true)
-
#
-
# time_zone_select( "user", "time_zone", nil, :default => "Pacific Time (US & Canada)" )
-
#
-
# time_zone_select( "user", 'time_zone', ActiveSupport::TimeZone.us_zones, :default => "Pacific Time (US & Canada)")
-
#
-
# time_zone_select( "user", 'time_zone', [ ActiveSupport::TimeZone['Alaska'], ActiveSupport::TimeZone['Hawaii'] ])
-
#
-
# time_zone_select( "user", 'time_zone', /Australia/)
-
#
-
# time_zone_select( "user", "time_zone", ActiveSupport::TimeZone.all.sort, :model => ActiveSupport::TimeZone)
-
1
def time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {})
-
InstanceTag.new(object, method, self, options.delete(:object)).to_time_zone_select_tag(priority_zones, options, html_options)
-
end
-
-
# Accepts a container (hash, array, enumerable, your type) and returns a string of option tags. Given a container
-
# where the elements respond to first and last (such as a two-element array), the "lasts" serve as option values and
-
# the "firsts" as option text. Hashes are turned into this form automatically, so the keys become "firsts" and values
-
# become lasts. If +selected+ is specified, the matching "last" or element will get the selected option-tag. +selected+
-
# may also be an array of values to be selected when using a multiple select.
-
#
-
# Examples (call, result):
-
# options_for_select([["Dollar", "$"], ["Kroner", "DKK"]])
-
# <option value="$">Dollar</option>\n<option value="DKK">Kroner</option>
-
#
-
# options_for_select([ "VISA", "MasterCard" ], "MasterCard")
-
# <option>VISA</option>\n<option selected="selected">MasterCard</option>
-
#
-
# options_for_select({ "Basic" => "$20", "Plus" => "$40" }, "$40")
-
# <option value="$20">Basic</option>\n<option value="$40" selected="selected">Plus</option>
-
#
-
# options_for_select([ "VISA", "MasterCard", "Discover" ], ["VISA", "Discover"])
-
# <option selected="selected">VISA</option>\n<option>MasterCard</option>\n<option selected="selected">Discover</option>
-
#
-
# You can optionally provide html attributes as the last element of the array.
-
#
-
# Examples:
-
# options_for_select([ "Denmark", ["USA", {:class => 'bold'}], "Sweden" ], ["USA", "Sweden"])
-
# <option value="Denmark">Denmark</option>\n<option value="USA" class="bold" selected="selected">USA</option>\n<option value="Sweden" selected="selected">Sweden</option>
-
#
-
# options_for_select([["Dollar", "$", {:class => "bold"}], ["Kroner", "DKK", {:onclick => "alert('HI');"}]])
-
# <option value="$" class="bold">Dollar</option>\n<option value="DKK" onclick="alert('HI');">Kroner</option>
-
#
-
# If you wish to specify disabled option tags, set +selected+ to be a hash, with <tt>:disabled</tt> being either a value
-
# or array of values to be disabled. In this case, you can use <tt>:selected</tt> to specify selected option tags.
-
#
-
# Examples:
-
# options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], :disabled => "Super Platinum")
-
# <option value="Free">Free</option>\n<option value="Basic">Basic</option>\n<option value="Advanced">Advanced</option>\n<option value="Super Platinum" disabled="disabled">Super Platinum</option>
-
#
-
# options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], :disabled => ["Advanced", "Super Platinum"])
-
# <option value="Free">Free</option>\n<option value="Basic">Basic</option>\n<option value="Advanced" disabled="disabled">Advanced</option>\n<option value="Super Platinum" disabled="disabled">Super Platinum</option>
-
#
-
# options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], :selected => "Free", :disabled => "Super Platinum")
-
# <option value="Free" selected="selected">Free</option>\n<option value="Basic">Basic</option>\n<option value="Advanced">Advanced</option>\n<option value="Super Platinum" disabled="disabled">Super Platinum</option>
-
#
-
# NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
-
1
def options_for_select(container, selected = nil)
-
return container if String === container
-
-
selected, disabled = extract_selected_and_disabled(selected).map do | r |
-
Array.wrap(r).map { |item| item.to_s }
-
end
-
-
container.map do |element|
-
html_attributes = option_html_attributes(element)
-
text, value = option_text_and_value(element).map { |item| item.to_s }
-
selected_attribute = ' selected="selected"' if option_value_selected?(value, selected)
-
disabled_attribute = ' disabled="disabled"' if disabled && option_value_selected?(value, disabled)
-
%(<option value="#{ERB::Util.html_escape(value)}"#{selected_attribute}#{disabled_attribute}#{html_attributes}>#{ERB::Util.html_escape(text)}</option>)
-
end.join("\n").html_safe
-
-
end
-
-
# Returns a string of option tags that have been compiled by iterating over the +collection+ and assigning the
-
# the result of a call to the +value_method+ as the option value and the +text_method+ as the option text.
-
# Example:
-
# options_from_collection_for_select(@people, 'id', 'name')
-
# This will output the same HTML as if you did this:
-
# <option value="#{person.id}">#{person.name}</option>
-
#
-
# This is more often than not used inside a #select_tag like this example:
-
# select_tag 'person', options_from_collection_for_select(@people, 'id', 'name')
-
#
-
# If +selected+ is specified as a value or array of values, the element(s) returning a match on +value_method+
-
# will be selected option tag(s).
-
#
-
# If +selected+ is specified as a Proc, those members of the collection that return true for the anonymous
-
# function are the selected values.
-
#
-
# +selected+ can also be a hash, specifying both <tt>:selected</tt> and/or <tt>:disabled</tt> values as required.
-
#
-
# Be sure to specify the same class as the +value_method+ when specifying selected or disabled options.
-
# Failure to do this will produce undesired results. Example:
-
# options_from_collection_for_select(@people, 'id', 'name', '1')
-
# Will not select a person with the id of 1 because 1 (an Integer) is not the same as '1' (a string)
-
# options_from_collection_for_select(@people, 'id', 'name', 1)
-
# should produce the desired results.
-
1
def options_from_collection_for_select(collection, value_method, text_method, selected = nil)
-
options = collection.map do |element|
-
[element.send(text_method), element.send(value_method)]
-
end
-
selected, disabled = extract_selected_and_disabled(selected)
-
select_deselect = {}
-
select_deselect[:selected] = extract_values_from_collection(collection, value_method, selected)
-
select_deselect[:disabled] = extract_values_from_collection(collection, value_method, disabled)
-
-
options_for_select(options, select_deselect)
-
end
-
-
# Returns a string of <tt><option></tt> tags, like <tt>options_from_collection_for_select</tt>, but
-
# groups them by <tt><optgroup></tt> tags based on the object relationships of the arguments.
-
#
-
# Parameters:
-
# * +collection+ - An array of objects representing the <tt><optgroup></tt> tags.
-
# * +group_method+ - The name of a method which, when called on a member of +collection+, returns an
-
# array of child objects representing the <tt><option></tt> tags.
-
# * group_label_method+ - The name of a method which, when called on a member of +collection+, returns a
-
# string to be used as the +label+ attribute for its <tt><optgroup></tt> tag.
-
# * +option_key_method+ - The name of a method which, when called on a child object of a member of
-
# +collection+, returns a value to be used as the +value+ attribute for its <tt><option></tt> tag.
-
# * +option_value_method+ - The name of a method which, when called on a child object of a member of
-
# +collection+, returns a value to be used as the contents of its <tt><option></tt> tag.
-
# * +selected_key+ - A value equal to the +value+ attribute for one of the <tt><option></tt> tags,
-
# which will have the +selected+ attribute set. Corresponds to the return value of one of the calls
-
# to +option_key_method+. If +nil+, no selection is made. Can also be a hash if disabled values are
-
# to be specified.
-
#
-
# Example object structure for use with this method:
-
# class Continent < ActiveRecord::Base
-
# has_many :countries
-
# # attribs: id, name
-
# end
-
# class Country < ActiveRecord::Base
-
# belongs_to :continent
-
# # attribs: id, name, continent_id
-
# end
-
#
-
# Sample usage:
-
# option_groups_from_collection_for_select(@continents, :countries, :name, :id, :name, 3)
-
#
-
# Possible output:
-
# <optgroup label="Africa">
-
# <option value="1">Egypt</option>
-
# <option value="4">Rwanda</option>
-
# ...
-
# </optgroup>
-
# <optgroup label="Asia">
-
# <option value="3" selected="selected">China</option>
-
# <option value="12">India</option>
-
# <option value="5">Japan</option>
-
# ...
-
# </optgroup>
-
#
-
# <b>Note:</b> Only the <tt><optgroup></tt> and <tt><option></tt> tags are returned, so you still have to
-
# wrap the output in an appropriate <tt><select></tt> tag.
-
1
def option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, selected_key = nil)
-
collection.map do |group|
-
group_label_string = eval("group.#{group_label_method}")
-
"<optgroup label=\"#{ERB::Util.html_escape(group_label_string)}\">" +
-
options_from_collection_for_select(eval("group.#{group_method}"), option_key_method, option_value_method, selected_key) +
-
'</optgroup>'
-
end.join.html_safe
-
end
-
-
# Returns a string of <tt><option></tt> tags, like <tt>options_for_select</tt>, but
-
# wraps them with <tt><optgroup></tt> tags.
-
#
-
# Parameters:
-
# * +grouped_options+ - Accepts a nested array or hash of strings. The first value serves as the
-
# <tt><optgroup></tt> label while the second value must be an array of options. The second value can be a
-
# nested array of text-value pairs. See <tt>options_for_select</tt> for more info.
-
# Ex. ["North America",[["United States","US"],["Canada","CA"]]]
-
# * +selected_key+ - A value equal to the +value+ attribute for one of the <tt><option></tt> tags,
-
# which will have the +selected+ attribute set. Note: It is possible for this value to match multiple options
-
# as you might have the same option in multiple groups. Each will then get <tt>selected="selected"</tt>.
-
# * +prompt+ - set to true or a prompt string. When the select element doesn't have a value yet, this
-
# prepends an option with a generic prompt - "Please select" - or the given prompt string.
-
#
-
# Sample usage (Array):
-
# grouped_options = [
-
# ['North America',
-
# [['United States','US'],'Canada']],
-
# ['Europe',
-
# ['Denmark','Germany','France']]
-
# ]
-
# grouped_options_for_select(grouped_options)
-
#
-
# Sample usage (Hash):
-
# grouped_options = {
-
# 'North America' => [['United States','US'], 'Canada'],
-
# 'Europe' => ['Denmark','Germany','France']
-
# }
-
# grouped_options_for_select(grouped_options)
-
#
-
# Possible output:
-
# <optgroup label="Europe">
-
# <option value="Denmark">Denmark</option>
-
# <option value="Germany">Germany</option>
-
# <option value="France">France</option>
-
# </optgroup>
-
# <optgroup label="North America">
-
# <option value="US">United States</option>
-
# <option value="Canada">Canada</option>
-
# </optgroup>
-
#
-
# <b>Note:</b> Only the <tt><optgroup></tt> and <tt><option></tt> tags are returned, so you still have to
-
# wrap the output in an appropriate <tt><select></tt> tag.
-
1
def grouped_options_for_select(grouped_options, selected_key = nil, prompt = nil)
-
body = ''
-
body << content_tag(:option, prompt, { :value => "" }, true) if prompt
-
-
grouped_options = grouped_options.sort if grouped_options.is_a?(Hash)
-
-
grouped_options.each do |group|
-
body << content_tag(:optgroup, options_for_select(group[1], selected_key), :label => group[0])
-
end
-
-
body.html_safe
-
end
-
-
# Returns a string of option tags for pretty much any time zone in the
-
# world. Supply a ActiveSupport::TimeZone name as +selected+ to have it
-
# marked as the selected option tag. You can also supply an array of
-
# ActiveSupport::TimeZone objects as +priority_zones+, so that they will
-
# be listed above the rest of the (long) list. (You can use
-
# ActiveSupport::TimeZone.us_zones as a convenience for obtaining a list
-
# of the US time zones, or a Regexp to select the zones of your choice)
-
#
-
# The +selected+ parameter must be either +nil+, or a string that names
-
# a ActiveSupport::TimeZone.
-
#
-
# By default, +model+ is the ActiveSupport::TimeZone constant (which can
-
# be obtained in Active Record as a value object). The only requirement
-
# is that the +model+ parameter be an object that responds to +all+, and
-
# returns an array of objects that represent time zones.
-
#
-
# NOTE: Only the option tags are returned, you have to wrap this call in
-
# a regular HTML select tag.
-
1
def time_zone_options_for_select(selected = nil, priority_zones = nil, model = ::ActiveSupport::TimeZone)
-
zone_options = ""
-
-
zones = model.all
-
convert_zones = lambda { |list| list.map { |z| [ z.to_s, z.name ] } }
-
-
if priority_zones
-
if priority_zones.is_a?(Regexp)
-
priority_zones = model.all.find_all {|z| z =~ priority_zones}
-
end
-
zone_options += options_for_select(convert_zones[priority_zones], selected)
-
zone_options += "<option value=\"\" disabled=\"disabled\">-------------</option>\n"
-
-
zones = zones.reject { |z| priority_zones.include?( z ) }
-
end
-
-
zone_options += options_for_select(convert_zones[zones], selected)
-
zone_options.html_safe
-
end
-
-
1
private
-
1
def option_html_attributes(element)
-
return "" unless Array === element
-
html_attributes = []
-
element.select { |e| Hash === e }.reduce({}, :merge).each do |k, v|
-
html_attributes << " #{k}=\"#{ERB::Util.html_escape(v.to_s)}\""
-
end
-
html_attributes.join
-
end
-
-
1
def option_text_and_value(option)
-
# Options are [text, value] pairs or strings used for both.
-
case
-
when Array === option
-
option = option.reject { |e| Hash === e }
-
[option.first, option.last]
-
when !option.is_a?(String) && option.respond_to?(:first) && option.respond_to?(:last)
-
[option.first, option.last]
-
else
-
[option, option]
-
end
-
end
-
-
1
def option_value_selected?(value, selected)
-
if selected.respond_to?(:include?) && !selected.is_a?(String)
-
selected.include? value
-
else
-
value == selected
-
end
-
end
-
-
1
def extract_selected_and_disabled(selected)
-
if selected.is_a?(Proc)
-
[ selected, nil ]
-
else
-
selected = Array.wrap(selected)
-
options = selected.extract_options!.symbolize_keys
-
[ options.include?(:selected) ? options[:selected] : selected, options[:disabled] ]
-
end
-
end
-
-
1
def extract_values_from_collection(collection, value_method, selected)
-
if selected.is_a?(Proc)
-
collection.map do |element|
-
element.send(value_method) if selected.call(element)
-
end.compact
-
else
-
selected
-
end
-
end
-
end
-
-
1
class InstanceTag #:nodoc:
-
1
include FormOptionsHelper
-
-
1
def to_select_tag(choices, options, html_options)
-
html_options = html_options.stringify_keys
-
add_default_name_and_id(html_options)
-
value = value(object)
-
selected_value = options.has_key?(:selected) ? options[:selected] : value
-
disabled_value = options.has_key?(:disabled) ? options[:disabled] : nil
-
content_tag("select", add_options(options_for_select(choices, :selected => selected_value, :disabled => disabled_value), options, selected_value), html_options)
-
end
-
-
1
def to_collection_select_tag(collection, value_method, text_method, options, html_options)
-
html_options = html_options.stringify_keys
-
add_default_name_and_id(html_options)
-
value = value(object)
-
disabled_value = options.has_key?(:disabled) ? options[:disabled] : nil
-
selected_value = options.has_key?(:selected) ? options[:selected] : value
-
content_tag(
-
"select", add_options(options_from_collection_for_select(collection, value_method, text_method, :selected => selected_value, :disabled => disabled_value), options, value), html_options
-
)
-
end
-
-
1
def to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
-
html_options = html_options.stringify_keys
-
add_default_name_and_id(html_options)
-
value = value(object)
-
content_tag(
-
"select", add_options(option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, value), options, value), html_options
-
)
-
end
-
-
1
def to_time_zone_select_tag(priority_zones, options, html_options)
-
html_options = html_options.stringify_keys
-
add_default_name_and_id(html_options)
-
value = value(object)
-
content_tag("select",
-
add_options(
-
time_zone_options_for_select(value || options[:default], priority_zones, options[:model] || ActiveSupport::TimeZone),
-
options, value
-
), html_options
-
)
-
end
-
-
1
private
-
1
def add_options(option_tags, options, value = nil)
-
if options[:include_blank]
-
option_tags = "<option value=\"\">#{ERB::Util.html_escape(options[:include_blank]) if options[:include_blank].kind_of?(String)}</option>\n" + option_tags
-
end
-
if value.blank? && options[:prompt]
-
prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('helpers.select.prompt', :default => 'Please select')
-
option_tags = "<option value=\"\">#{ERB::Util.html_escape(prompt)}</option>\n" + option_tags
-
end
-
option_tags.html_safe
-
end
-
end
-
-
1
class FormBuilder
-
1
def select(method, choices, options = {}, html_options = {})
-
@template.select(@object_name, method, choices, objectify_options(options), @default_options.merge(html_options))
-
end
-
-
1
def collection_select(method, collection, value_method, text_method, options = {}, html_options = {})
-
@template.collection_select(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options))
-
end
-
-
1
def grouped_collection_select(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
-
@template.grouped_collection_select(@object_name, method, collection, group_method, group_label_method, option_key_method, option_value_method, objectify_options(options), @default_options.merge(html_options))
-
end
-
-
1
def time_zone_select(method, priority_zones = nil, options = {}, html_options = {})
-
@template.time_zone_select(@object_name, method, priority_zones, objectify_options(options), @default_options.merge(html_options))
-
end
-
end
-
end
-
end
-
1
require 'cgi'
-
1
require 'action_view/helpers/tag_helper'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/string/output_safety'
-
-
1
module ActionView
-
# = Action View Form Tag Helpers
-
1
module Helpers
-
# Provides a number of methods for creating form tags that doesn't rely on an Active Record object assigned to the template like
-
# FormHelper does. Instead, you provide the names and values manually.
-
#
-
# NOTE: The HTML options <tt>disabled</tt>, <tt>readonly</tt>, and <tt>multiple</tt> can all be treated as booleans. So specifying
-
# <tt>:disabled => true</tt> will give <tt>disabled="disabled"</tt>.
-
1
module FormTagHelper
-
1
extend ActiveSupport::Concern
-
-
1
include UrlHelper
-
1
include TextHelper
-
-
# Starts a form tag that points the action to an url configured with <tt>url_for_options</tt> just like
-
# ActionController::Base#url_for. The method for the form defaults to POST.
-
#
-
# ==== Options
-
# * <tt>:multipart</tt> - If set to true, the enctype is set to "multipart/form-data".
-
# * <tt>:method</tt> - The method to use when submitting the form, usually either "get" or "post".
-
# If "put", "delete", or another verb is used, a hidden input with name <tt>_method</tt>
-
# is added to simulate the verb over post.
-
# * <tt>:authenticity_token</tt> - Authenticity token to use in the form. Use only if you need to
-
# pass custom authenticity token string, or to not add authenticity_token field at all
-
# (by passing <tt>false</tt>).
-
# * A list of parameters to feed to the URL the form will be posted to.
-
# * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the
-
# submit behavior. By default this behavior is an ajax submit.
-
#
-
# ==== Examples
-
# form_tag('/posts')
-
# # => <form action="/posts" method="post">
-
#
-
# form_tag('/posts/1', :method => :put)
-
# # => <form action="/posts/1" method="put">
-
#
-
# form_tag('/upload', :multipart => true)
-
# # => <form action="/upload" method="post" enctype="multipart/form-data">
-
#
-
# <%= form_tag('/posts') do -%>
-
# <div><%= submit_tag 'Save' %></div>
-
# <% end -%>
-
# # => <form action="/posts" method="post"><div><input type="submit" name="submit" value="Save" /></div></form>
-
#
-
# <%= form_tag('/posts', :remote => true) %>
-
# # => <form action="/posts" method="post" data-remote="true">
-
#
-
# form_tag('http://far.away.com/form', :authenticity_token => false)
-
# # form without authenticity token
-
#
-
# form_tag('http://far.away.com/form', :authenticity_token => "cf50faa3fe97702ca1ae")
-
# # form with custom authenticity token
-
#
-
1
def form_tag(url_for_options = {}, options = {}, &block)
-
html_options = html_options_for_form(url_for_options, options)
-
if block_given?
-
form_tag_in_block(html_options, &block)
-
else
-
form_tag_html(html_options)
-
end
-
end
-
-
# Creates a dropdown selection box, or if the <tt>:multiple</tt> option is set to true, a multiple
-
# choice selection box.
-
#
-
# Helpers::FormOptions can be used to create common select boxes such as countries, time zones, or
-
# associated records. <tt>option_tags</tt> is a string containing the option tags for the select box.
-
#
-
# ==== Options
-
# * <tt>:multiple</tt> - If set to true the selection will allow multiple choices.
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
# * <tt>:include_blank</tt> - If set to true, an empty option will be create
-
# * <tt>:prompt</tt> - Create a prompt option with blank value and the text asking user to select something
-
# * Any other key creates standard HTML attributes for the tag.
-
#
-
# ==== Examples
-
# select_tag "people", options_from_collection_for_select(@people, "id", "name")
-
# # <select id="people" name="people"><option value="1">David</option></select>
-
#
-
# select_tag "people", "<option>David</option>".html_safe
-
# # => <select id="people" name="people"><option>David</option></select>
-
#
-
# select_tag "count", "<option>1</option><option>2</option><option>3</option><option>4</option>".html_safe
-
# # => <select id="count" name="count"><option>1</option><option>2</option>
-
# # <option>3</option><option>4</option></select>
-
#
-
# select_tag "colors", "<option>Red</option><option>Green</option><option>Blue</option>".html_safe, :multiple => true
-
# # => <select id="colors" multiple="multiple" name="colors[]"><option>Red</option>
-
# # <option>Green</option><option>Blue</option></select>
-
#
-
# select_tag "locations", "<option>Home</option><option selected="selected">Work</option><option>Out</option>".html_safe
-
# # => <select id="locations" name="locations"><option>Home</option><option selected='selected'>Work</option>
-
# # <option>Out</option></select>
-
#
-
# select_tag "access", "<option>Read</option><option>Write</option>".html_safe, :multiple => true, :class => 'form_input'
-
# # => <select class="form_input" id="access" multiple="multiple" name="access[]"><option>Read</option>
-
# # <option>Write</option></select>
-
#
-
# select_tag "people", options_from_collection_for_select(@people, "id", "name"), :include_blank => true
-
# # => <select id="people" name="people"><option value=""></option><option value="1">David</option></select>
-
#
-
# select_tag "people", options_from_collection_for_select(@people, "id", "name"), :prompt => "Select something"
-
# # => <select id="people" name="people"><option value="">Select something</option><option value="1">David</option></select>
-
#
-
# select_tag "destination", "<option>NYC</option><option>Paris</option><option>Rome</option>".html_safe, :disabled => true
-
# # => <select disabled="disabled" id="destination" name="destination"><option>NYC</option>
-
# # <option>Paris</option><option>Rome</option></select>
-
1
def select_tag(name, option_tags = nil, options = {})
-
html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name
-
-
if options.delete(:include_blank)
-
option_tags = "<option value=\"\"></option>".html_safe + option_tags
-
end
-
-
if prompt = options.delete(:prompt)
-
option_tags = "<option value=\"\">#{prompt}</option>".html_safe + option_tags
-
end
-
-
content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
-
end
-
-
# Creates a standard text field; use these text fields to input smaller chunks of text like a username
-
# or a search query.
-
#
-
# ==== Options
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
# * <tt>:size</tt> - The number of visible characters that will fit in the input.
-
# * <tt>:maxlength</tt> - The maximum number of characters that the browser will allow the user to enter.
-
# * <tt>:placeholder</tt> - The text contained in the field by default which is removed when the field receives focus.
-
# * Any other key creates standard HTML attributes for the tag.
-
#
-
# ==== Examples
-
# text_field_tag 'name'
-
# # => <input id="name" name="name" type="text" />
-
#
-
# text_field_tag 'query', 'Enter your search query here'
-
# # => <input id="query" name="query" type="text" value="Enter your search query here" />
-
#
-
# text_field_tag 'search', nil, :placeholder => 'Enter search term...'
-
# # => <input id="search" name="search" placeholder="Enter search term..." type="text" />
-
#
-
# text_field_tag 'request', nil, :class => 'special_input'
-
# # => <input class="special_input" id="request" name="request" type="text" />
-
#
-
# text_field_tag 'address', '', :size => 75
-
# # => <input id="address" name="address" size="75" type="text" value="" />
-
#
-
# text_field_tag 'zip', nil, :maxlength => 5
-
# # => <input id="zip" maxlength="5" name="zip" type="text" />
-
#
-
# text_field_tag 'payment_amount', '$0.00', :disabled => true
-
# # => <input disabled="disabled" id="payment_amount" name="payment_amount" type="text" value="$0.00" />
-
#
-
# text_field_tag 'ip', '0.0.0.0', :maxlength => 15, :size => 20, :class => "ip-input"
-
# # => <input class="ip-input" id="ip" maxlength="15" name="ip" size="20" type="text" value="0.0.0.0" />
-
1
def text_field_tag(name, value = nil, options = {})
-
tag :input, { "type" => "text", "name" => name, "id" => sanitize_to_id(name), "value" => value }.update(options.stringify_keys)
-
end
-
-
# Creates a label element. Accepts a block.
-
#
-
# ==== Options
-
# * Creates standard HTML attributes for the tag.
-
#
-
# ==== Examples
-
# label_tag 'name'
-
# # => <label for="name">Name</label>
-
#
-
# label_tag 'name', 'Your name'
-
# # => <label for="name">Your Name</label>
-
#
-
# label_tag 'name', nil, :class => 'small_label'
-
# # => <label for="name" class="small_label">Name</label>
-
1
def label_tag(name = nil, content_or_options = nil, options = nil, &block)
-
options = content_or_options if block_given? && content_or_options.is_a?(Hash)
-
options ||= {}
-
options.stringify_keys!
-
options["for"] = sanitize_to_id(name) unless name.blank? || options.has_key?("for")
-
content_tag :label, content_or_options || name.to_s.humanize, options, &block
-
end
-
-
# Creates a hidden form input field used to transmit data that would be lost due to HTTP's statelessness or
-
# data that should be hidden from the user.
-
#
-
# ==== Options
-
# * Creates standard HTML attributes for the tag.
-
#
-
# ==== Examples
-
# hidden_field_tag 'tags_list'
-
# # => <input id="tags_list" name="tags_list" type="hidden" />
-
#
-
# hidden_field_tag 'token', 'VUBJKB23UIVI1UU1VOBVI@'
-
# # => <input id="token" name="token" type="hidden" value="VUBJKB23UIVI1UU1VOBVI@" />
-
#
-
# hidden_field_tag 'collected_input', '', :onchange => "alert('Input collected!')"
-
# # => <input id="collected_input" name="collected_input" onchange="alert('Input collected!')"
-
# # type="hidden" value="" />
-
1
def hidden_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "hidden"))
-
end
-
-
# Creates a file upload field. If you are using file uploads then you will also need
-
# to set the multipart option for the form tag:
-
#
-
# <%= form_tag '/upload', :multipart => true do %>
-
# <label for="file">File to Upload</label> <%= file_field_tag "file" %>
-
# <%= submit_tag %>
-
# <% end %>
-
#
-
# The specified URL will then be passed a File object containing the selected file, or if the field
-
# was left blank, a StringIO object.
-
#
-
# ==== Options
-
# * Creates standard HTML attributes for the tag.
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
#
-
# ==== Examples
-
# file_field_tag 'attachment'
-
# # => <input id="attachment" name="attachment" type="file" />
-
#
-
# file_field_tag 'avatar', :class => 'profile_input'
-
# # => <input class="profile_input" id="avatar" name="avatar" type="file" />
-
#
-
# file_field_tag 'picture', :disabled => true
-
# # => <input disabled="disabled" id="picture" name="picture" type="file" />
-
#
-
# file_field_tag 'resume', :value => '~/resume.doc'
-
# # => <input id="resume" name="resume" type="file" value="~/resume.doc" />
-
#
-
# file_field_tag 'user_pic', :accept => 'image/png,image/gif,image/jpeg'
-
# # => <input accept="image/png,image/gif,image/jpeg" id="user_pic" name="user_pic" type="file" />
-
#
-
# file_field_tag 'file', :accept => 'text/html', :class => 'upload', :value => 'index.html'
-
# # => <input accept="text/html" class="upload" id="file" name="file" type="file" value="index.html" />
-
1
def file_field_tag(name, options = {})
-
text_field_tag(name, nil, options.update("type" => "file"))
-
end
-
-
# Creates a password field, a masked text field that will hide the users input behind a mask character.
-
#
-
# ==== Options
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
# * <tt>:size</tt> - The number of visible characters that will fit in the input.
-
# * <tt>:maxlength</tt> - The maximum number of characters that the browser will allow the user to enter.
-
# * Any other key creates standard HTML attributes for the tag.
-
#
-
# ==== Examples
-
# password_field_tag 'pass'
-
# # => <input id="pass" name="pass" type="password" />
-
#
-
# password_field_tag 'secret', 'Your secret here'
-
# # => <input id="secret" name="secret" type="password" value="Your secret here" />
-
#
-
# password_field_tag 'masked', nil, :class => 'masked_input_field'
-
# # => <input class="masked_input_field" id="masked" name="masked" type="password" />
-
#
-
# password_field_tag 'token', '', :size => 15
-
# # => <input id="token" name="token" size="15" type="password" value="" />
-
#
-
# password_field_tag 'key', nil, :maxlength => 16
-
# # => <input id="key" maxlength="16" name="key" type="password" />
-
#
-
# password_field_tag 'confirm_pass', nil, :disabled => true
-
# # => <input disabled="disabled" id="confirm_pass" name="confirm_pass" type="password" />
-
#
-
# password_field_tag 'pin', '1234', :maxlength => 4, :size => 6, :class => "pin_input"
-
# # => <input class="pin_input" id="pin" maxlength="4" name="pin" size="6" type="password" value="1234" />
-
1
def password_field_tag(name = "password", value = nil, options = {})
-
text_field_tag(name, value, options.update("type" => "password"))
-
end
-
-
# Creates a text input area; use a textarea for longer text inputs such as blog posts or descriptions.
-
#
-
# ==== Options
-
# * <tt>:size</tt> - A string specifying the dimensions (columns by rows) of the textarea (e.g., "25x10").
-
# * <tt>:rows</tt> - Specify the number of rows in the textarea
-
# * <tt>:cols</tt> - Specify the number of columns in the textarea
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
# * <tt>:escape</tt> - By default, the contents of the text input are HTML escaped.
-
# If you need unescaped contents, set this to false.
-
# * Any other key creates standard HTML attributes for the tag.
-
#
-
# ==== Examples
-
# text_area_tag 'post'
-
# # => <textarea id="post" name="post"></textarea>
-
#
-
# text_area_tag 'bio', @user.bio
-
# # => <textarea id="bio" name="bio">This is my biography.</textarea>
-
#
-
# text_area_tag 'body', nil, :rows => 10, :cols => 25
-
# # => <textarea cols="25" id="body" name="body" rows="10"></textarea>
-
#
-
# text_area_tag 'body', nil, :size => "25x10"
-
# # => <textarea name="body" id="body" cols="25" rows="10"></textarea>
-
#
-
# text_area_tag 'description', "Description goes here.", :disabled => true
-
# # => <textarea disabled="disabled" id="description" name="description">Description goes here.</textarea>
-
#
-
# text_area_tag 'comment', nil, :class => 'comment_input'
-
# # => <textarea class="comment_input" id="comment" name="comment"></textarea>
-
1
def text_area_tag(name, content = nil, options = {})
-
options.stringify_keys!
-
-
if size = options.delete("size")
-
options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
-
end
-
-
escape = options.key?("escape") ? options.delete("escape") : true
-
content = ERB::Util.html_escape(content) if escape
-
-
content_tag :textarea, content.to_s.html_safe, { "name" => name, "id" => sanitize_to_id(name) }.update(options)
-
end
-
-
# Creates a check box form input tag.
-
#
-
# ==== Options
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
# * Any other key creates standard HTML options for the tag.
-
#
-
# ==== Examples
-
# check_box_tag 'accept'
-
# # => <input id="accept" name="accept" type="checkbox" value="1" />
-
#
-
# check_box_tag 'rock', 'rock music'
-
# # => <input id="rock" name="rock" type="checkbox" value="rock music" />
-
#
-
# check_box_tag 'receive_email', 'yes', true
-
# # => <input checked="checked" id="receive_email" name="receive_email" type="checkbox" value="yes" />
-
#
-
# check_box_tag 'tos', 'yes', false, :class => 'accept_tos'
-
# # => <input class="accept_tos" id="tos" name="tos" type="checkbox" value="yes" />
-
#
-
# check_box_tag 'eula', 'accepted', false, :disabled => true
-
# # => <input disabled="disabled" id="eula" name="eula" type="checkbox" value="accepted" />
-
1
def check_box_tag(name, value = "1", checked = false, options = {})
-
html_options = { "type" => "checkbox", "name" => name, "id" => sanitize_to_id(name), "value" => value }.update(options.stringify_keys)
-
html_options["checked"] = "checked" if checked
-
tag :input, html_options
-
end
-
-
# Creates a radio button; use groups of radio buttons named the same to allow users to
-
# select from a group of options.
-
#
-
# ==== Options
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
# * Any other key creates standard HTML options for the tag.
-
#
-
# ==== Examples
-
# radio_button_tag 'gender', 'male'
-
# # => <input id="gender_male" name="gender" type="radio" value="male" />
-
#
-
# radio_button_tag 'receive_updates', 'no', true
-
# # => <input checked="checked" id="receive_updates_no" name="receive_updates" type="radio" value="no" />
-
#
-
# radio_button_tag 'time_slot', "3:00 p.m.", false, :disabled => true
-
# # => <input disabled="disabled" id="time_slot_300_pm" name="time_slot" type="radio" value="3:00 p.m." />
-
#
-
# radio_button_tag 'color', "green", true, :class => "color_input"
-
# # => <input checked="checked" class="color_input" id="color_green" name="color" type="radio" value="green" />
-
1
def radio_button_tag(name, value, checked = false, options = {})
-
html_options = { "type" => "radio", "name" => name, "id" => "#{sanitize_to_id(name)}_#{sanitize_to_id(value)}", "value" => value }.update(options.stringify_keys)
-
html_options["checked"] = "checked" if checked
-
tag :input, html_options
-
end
-
-
# Creates a submit button with the text <tt>value</tt> as the caption.
-
#
-
# ==== Options
-
# * <tt>:confirm => 'question?'</tt> - If present the unobtrusive JavaScript
-
# drivers will provide a prompt with the question specified. If the user accepts,
-
# the form is processed normally, otherwise no action is taken.
-
# * <tt>:disabled</tt> - If true, the user will not be able to use this input.
-
# * <tt>:disable_with</tt> - Value of this parameter will be used as the value for a
-
# disabled version of the submit button when the form is submitted. This feature is
-
# provided by the unobtrusive JavaScript driver.
-
# * Any other key creates standard HTML options for the tag.
-
#
-
# ==== Examples
-
# submit_tag
-
# # => <input name="commit" type="submit" value="Save changes" />
-
#
-
# submit_tag "Edit this article"
-
# # => <input name="commit" type="submit" value="Edit this article" />
-
#
-
# submit_tag "Save edits", :disabled => true
-
# # => <input disabled="disabled" name="commit" type="submit" value="Save edits" />
-
#
-
#
-
# submit_tag "Complete sale", :disable_with => "Please wait..."
-
# # => <input name="commit" data-disable-with="Please wait..."
-
# # type="submit" value="Complete sale" />
-
#
-
# submit_tag nil, :class => "form_submit"
-
# # => <input class="form_submit" name="commit" type="submit" />
-
#
-
# submit_tag "Edit", :disable_with => "Editing...", :class => "edit_button"
-
# # => <input class="edit_button" data-disable_with="Editing..."
-
# # name="commit" type="submit" value="Edit" />
-
#
-
# submit_tag "Save", :confirm => "Are you sure?"
-
# # => <input name='commit' type='submit' value='Save'
-
# data-confirm="Are you sure?" />
-
#
-
1
def submit_tag(value = "Save changes", options = {})
-
options.stringify_keys!
-
-
if disable_with = options.delete("disable_with")
-
options["data-disable-with"] = disable_with
-
end
-
-
if confirm = options.delete("confirm")
-
options["data-confirm"] = confirm
-
end
-
-
tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options.stringify_keys)
-
end
-
-
# Creates a button element that defines a <tt>submit</tt> button,
-
# <tt>reset</tt>button or a generic button which can be used in
-
# JavaScript, for example. You can use the button tag as a regular
-
# submit tag but it isn't supported in legacy browsers. However,
-
# the button tag allows richer labels such as images and emphasis,
-
# so this helper will also accept a block.
-
#
-
# ==== Options
-
# * <tt>:confirm => 'question?'</tt> - If present, the
-
# unobtrusive JavaScript drivers will provide a prompt with
-
# the question specified. If the user accepts, the form is
-
# processed normally, otherwise no action is taken.
-
# * <tt>:disabled</tt> - If true, the user will not be able to
-
# use this input.
-
# * <tt>:disable_with</tt> - Value of this parameter will be
-
# used as the value for a disabled version of the submit
-
# button when the form is submitted. This feature is provided
-
# by the unobtrusive JavaScript driver.
-
# * Any other key creates standard HTML options for the tag.
-
#
-
# ==== Examples
-
# button_tag
-
# # => <button name="button" type="submit">Button</button>
-
#
-
# button_tag(:type => 'button') do
-
# content_tag(:strong, 'Ask me!')
-
# end
-
# # => <button name="button" type="button">
-
# <strong>Ask me!</strong>
-
# </button>
-
#
-
# button_tag "Checkout", :disable_with => "Please wait..."
-
# # => <button data-disable-with="Please wait..." name="button"
-
# type="submit">Checkout</button>
-
#
-
1
def button_tag(content_or_options = nil, options = nil, &block)
-
options = content_or_options if block_given? && content_or_options.is_a?(Hash)
-
options ||= {}
-
options.stringify_keys!
-
-
if disable_with = options.delete("disable_with")
-
options["data-disable-with"] = disable_with
-
end
-
-
if confirm = options.delete("confirm")
-
options["data-confirm"] = confirm
-
end
-
-
options.reverse_merge! 'name' => 'button', 'type' => 'submit'
-
-
content_tag :button, content_or_options || 'Button', options, &block
-
end
-
-
# Displays an image which when clicked will submit the form.
-
#
-
# <tt>source</tt> is passed to AssetTagHelper#path_to_image
-
#
-
# ==== Options
-
# * <tt>:confirm => 'question?'</tt> - This will add a JavaScript confirm
-
# prompt with the question specified. If the user accepts, the form is
-
# processed normally, otherwise no action is taken.
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
# * Any other key creates standard HTML options for the tag.
-
#
-
# ==== Examples
-
# image_submit_tag("login.png")
-
# # => <input src="/images/login.png" type="image" />
-
#
-
# image_submit_tag("purchase.png", :disabled => true)
-
# # => <input disabled="disabled" src="/images/purchase.png" type="image" />
-
#
-
# image_submit_tag("search.png", :class => 'search_button')
-
# # => <input class="search_button" src="/images/search.png" type="image" />
-
#
-
# image_submit_tag("agree.png", :disabled => true, :class => "agree_disagree_button")
-
# # => <input class="agree_disagree_button" disabled="disabled" src="/images/agree.png" type="image" />
-
1
def image_submit_tag(source, options = {})
-
options.stringify_keys!
-
-
if confirm = options.delete("confirm")
-
options["data-confirm"] = confirm
-
end
-
-
tag :input, { "type" => "image", "src" => path_to_image(source) }.update(options.stringify_keys)
-
end
-
-
# Creates a field set for grouping HTML form elements.
-
#
-
# <tt>legend</tt> will become the fieldset's title (optional as per W3C).
-
# <tt>options</tt> accept the same values as tag.
-
#
-
# ==== Examples
-
# <%= field_set_tag do %>
-
# <p><%= text_field_tag 'name' %></p>
-
# <% end %>
-
# # => <fieldset><p><input id="name" name="name" type="text" /></p></fieldset>
-
#
-
# <%= field_set_tag 'Your details' do %>
-
# <p><%= text_field_tag 'name' %></p>
-
# <% end %>
-
# # => <fieldset><legend>Your details</legend><p><input id="name" name="name" type="text" /></p></fieldset>
-
#
-
# <%= field_set_tag nil, :class => 'format' do %>
-
# <p><%= text_field_tag 'name' %></p>
-
# <% end %>
-
# # => <fieldset class="format"><p><input id="name" name="name" type="text" /></p></fieldset>
-
1
def field_set_tag(legend = nil, options = nil, &block)
-
content = capture(&block)
-
output = tag(:fieldset, options, true)
-
output.safe_concat(content_tag(:legend, legend)) unless legend.blank?
-
output.concat(content)
-
output.safe_concat("</fieldset>")
-
end
-
-
# Creates a text field of type "search".
-
#
-
# ==== Options
-
# * Accepts the same options as text_field_tag.
-
1
def search_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "search"))
-
end
-
-
# Creates a text field of type "tel".
-
#
-
# ==== Options
-
# * Accepts the same options as text_field_tag.
-
1
def telephone_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "tel"))
-
end
-
1
alias phone_field_tag telephone_field_tag
-
-
# Creates a text field of type "url".
-
#
-
# ==== Options
-
# * Accepts the same options as text_field_tag.
-
1
def url_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "url"))
-
end
-
-
# Creates a text field of type "email".
-
#
-
# ==== Options
-
# * Accepts the same options as text_field_tag.
-
1
def email_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "email"))
-
end
-
-
# Creates a number field.
-
#
-
# ==== Options
-
# * <tt>:min</tt> - The minimum acceptable value.
-
# * <tt>:max</tt> - The maximum acceptable value.
-
# * <tt>:in</tt> - A range specifying the <tt>:min</tt> and
-
# <tt>:max</tt> values.
-
# * <tt>:step</tt> - The acceptable value granularity.
-
# * Otherwise accepts the same options as text_field_tag.
-
#
-
# ==== Examples
-
# number_field_tag 'quantity', nil, :in => 1...10
-
# => <input id="quantity" name="quantity" min="1" max="9" />
-
1
def number_field_tag(name, value = nil, options = {})
-
options = options.stringify_keys
-
options["type"] ||= "number"
-
if range = options.delete("in") || options.delete("within")
-
options.update("min" => range.min, "max" => range.max)
-
end
-
text_field_tag(name, value, options)
-
end
-
-
# Creates a range form element.
-
#
-
# ==== Options
-
# * Accepts the same options as number_field_tag.
-
1
def range_field_tag(name, value = nil, options = {})
-
number_field_tag(name, value, options.stringify_keys.update("type" => "range"))
-
end
-
-
1
private
-
1
def html_options_for_form(url_for_options, options)
-
options.stringify_keys.tap do |html_options|
-
html_options["enctype"] = "multipart/form-data" if html_options.delete("multipart")
-
# The following URL is unescaped, this is just a hash of options, and it is the
-
# responsibility of the caller to escape all the values.
-
html_options["action"] = url_for(url_for_options)
-
html_options["accept-charset"] = "UTF-8"
-
html_options["data-remote"] = true if html_options.delete("remote")
-
html_options["authenticity_token"] = html_options.delete("authenticity_token") if html_options.has_key?("authenticity_token")
-
end
-
end
-
-
1
def extra_tags_for_form(html_options)
-
snowman_tag = tag(:input, :type => "hidden",
-
:name => "utf8", :value => "✓".html_safe)
-
-
authenticity_token = html_options.delete("authenticity_token")
-
method = html_options.delete("method").to_s
-
-
method_tag = case method
-
when /^get$/i # must be case-insensitive, but can't use downcase as might be nil
-
html_options["method"] = "get"
-
''
-
when /^post$/i, "", nil
-
html_options["method"] = "post"
-
token_tag(authenticity_token)
-
else
-
html_options["method"] = "post"
-
tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag(authenticity_token)
-
end
-
-
tags = snowman_tag << method_tag
-
content_tag(:div, tags, :style => 'margin:0;padding:0;display:inline')
-
end
-
-
1
def form_tag_html(html_options)
-
extra_tags = extra_tags_for_form(html_options)
-
(tag(:form, html_options, true) + extra_tags).html_safe
-
end
-
-
1
def form_tag_in_block(html_options, &block)
-
content = capture(&block)
-
output = ActiveSupport::SafeBuffer.new
-
output.safe_concat(form_tag_html(html_options))
-
output << content
-
output.safe_concat("</form>")
-
end
-
-
1
def token_tag(token)
-
if token == false || !protect_against_forgery?
-
''
-
else
-
token = form_authenticity_token if token.nil?
-
tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => token)
-
end
-
end
-
-
# see http://www.w3.org/TR/html4/types.html#type-name
-
1
def sanitize_to_id(name)
-
name.to_s.gsub(']','').gsub(/[^-a-zA-Z0-9:.]/, "_")
-
end
-
end
-
end
-
end
-
1
require 'action_view/helpers/tag_helper'
-
-
1
module ActionView
-
1
module Helpers
-
1
module JavaScriptHelper
-
1
JS_ESCAPE_MAP = {
-
'\\' => '\\\\',
-
'</' => '<\/',
-
"\r\n" => '\n',
-
"\n" => '\n',
-
"\r" => '\n',
-
'"' => '\\"',
-
"'" => "\\'" }
-
-
# Escape carrier returns and single and double quotes for JavaScript segments.
-
# Also available through the alias j(). This is particularly helpful in JavaScript responses, like:
-
#
-
# $('some_element').replaceWith('<%=j render 'some/element_template' %>');
-
1
def escape_javascript(javascript)
-
if javascript
-
result = javascript.gsub(/(\\|<\/|\r\n|[\n\r"'])/) {|match| JS_ESCAPE_MAP[match] }
-
javascript.html_safe? ? result.html_safe : result
-
else
-
''
-
end
-
end
-
-
1
alias_method :j, :escape_javascript
-
-
# Returns a JavaScript tag with the +content+ inside. Example:
-
# javascript_tag "alert('All is good')"
-
#
-
# Returns:
-
# <script type="text/javascript">
-
# //<![CDATA[
-
# alert('All is good')
-
# //]]>
-
# </script>
-
#
-
# +html_options+ may be a hash of attributes for the <tt>\<script></tt>
-
# tag. Example:
-
# javascript_tag "alert('All is good')", :defer => 'defer'
-
# # => <script defer="defer" type="text/javascript">alert('All is good')</script>
-
#
-
# Instead of passing the content as an argument, you can also use a block
-
# in which case, you pass your +html_options+ as the first parameter.
-
# <%= javascript_tag :defer => 'defer' do -%>
-
# alert('All is good')
-
# <% end -%>
-
1
def javascript_tag(content_or_options_with_block = nil, html_options = {}, &block)
-
content =
-
if block_given?
-
html_options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
-
capture(&block)
-
else
-
content_or_options_with_block
-
end
-
-
content_tag(:script, javascript_cdata_section(content), html_options.merge(:type => Mime::JS))
-
end
-
-
1
def javascript_cdata_section(content) #:nodoc:
-
"\n//#{cdata_section("\n#{content}\n//")}\n".html_safe
-
end
-
-
# Returns a button whose +onclick+ handler triggers the passed JavaScript.
-
#
-
# The helper receives a name, JavaScript code, and an optional hash of HTML options. The
-
# name is used as button label and the JavaScript code goes into its +onclick+ attribute.
-
# If +html_options+ has an <tt>:onclick</tt>, that one is put before +function+.
-
#
-
# button_to_function "Greeting", "alert('Hello world!')", :class => "ok"
-
# # => <input class="ok" onclick="alert('Hello world!');" type="button" value="Greeting" />
-
#
-
1
def button_to_function(name, function=nil, html_options={})
-
onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};"
-
-
tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => onclick))
-
end
-
-
# Returns a link whose +onclick+ handler triggers the passed JavaScript.
-
#
-
# The helper receives a name, JavaScript code, and an optional hash of HTML options. The
-
# name is used as the link text and the JavaScript code goes into the +onclick+ attribute.
-
# If +html_options+ has an <tt>:onclick</tt>, that one is put before +function+. Once all
-
# the JavaScript is set, the helper appends "; return false;".
-
#
-
# The +href+ attribute of the tag is set to "#" unles +html_options+ has one.
-
#
-
# link_to_function "Greeting", "alert('Hello world!')", :class => "nav_link"
-
# # => <a class="nav_link" href="#" onclick="alert('Hello world!'); return false;">Greeting</a>
-
#
-
1
def link_to_function(name, function, html_options={})
-
onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function}; return false;"
-
href = html_options[:href] || '#'
-
-
content_tag(:a, name, html_options.merge(:href => href, :onclick => onclick))
-
end
-
end
-
end
-
end
-
# encoding: utf-8
-
-
1
require 'active_support/core_ext/big_decimal/conversions'
-
1
require 'active_support/core_ext/float/rounding'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/string/output_safety'
-
-
1
module ActionView
-
# = Action View Number Helpers
-
1
module Helpers #:nodoc:
-
-
# Provides methods for converting numbers into formatted strings.
-
# Methods are provided for phone numbers, currency, percentage,
-
# precision, positional notation, file size and pretty printing.
-
#
-
# Most methods expect a +number+ argument, and will return it
-
# unchanged if can't be converted into a valid number.
-
1
module NumberHelper
-
-
1
DEFAULT_CURRENCY_VALUES = { :format => "%u%n", :negative_format => "-%u%n", :unit => "$", :separator => ".", :delimiter => ",",
-
:precision => 2, :significant => false, :strip_insignificant_zeros => false }
-
-
# Raised when argument +number+ param given to the helpers is invalid and
-
# the option :raise is set to +true+.
-
1
class InvalidNumberError < StandardError
-
1
attr_accessor :number
-
1
def initialize(number)
-
@number = number
-
end
-
end
-
-
# Formats a +number+ into a US phone number (e.g., (555) 123-9876). You can customize the format
-
# in the +options+ hash.
-
#
-
# ==== Options
-
# * <tt>:area_code</tt> - Adds parentheses around the area code.
-
# * <tt>:delimiter</tt> - Specifies the delimiter to use (defaults to "-").
-
# * <tt>:extension</tt> - Specifies an extension to add to the end of the
-
# generated number.
-
# * <tt>:country_code</tt> - Sets the country code for the phone number.
-
#
-
# ==== Examples
-
# number_to_phone(5551234) # => 555-1234
-
# number_to_phone(1235551234) # => 123-555-1234
-
# number_to_phone(1235551234, :area_code => true) # => (123) 555-1234
-
# number_to_phone(1235551234, :delimiter => " ") # => 123 555 1234
-
# number_to_phone(1235551234, :area_code => true, :extension => 555) # => (123) 555-1234 x 555
-
# number_to_phone(1235551234, :country_code => 1) # => +1-123-555-1234
-
#
-
# number_to_phone(1235551234, :country_code => 1, :extension => 1343, :delimiter => ".")
-
# => +1.123.555.1234 x 1343
-
1
def number_to_phone(number, options = {})
-
return unless number
-
-
begin
-
Float(number)
-
rescue ArgumentError, TypeError
-
raise InvalidNumberError, number
-
end if options[:raise]
-
-
number = number.to_s.strip
-
options = options.symbolize_keys
-
area_code = options[:area_code]
-
delimiter = options[:delimiter] || "-"
-
extension = options[:extension]
-
country_code = options[:country_code]
-
-
if area_code
-
number.gsub!(/(\d{1,3})(\d{3})(\d{4}$)/,"(\\1) \\2#{delimiter}\\3")
-
else
-
number.gsub!(/(\d{0,3})(\d{3})(\d{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
-
number.slice!(0, 1) if number.starts_with?('-')
-
end
-
-
str = []
-
str << "+#{country_code}#{delimiter}" unless country_code.blank?
-
str << number
-
str << " x #{extension}" unless extension.blank?
-
ERB::Util.html_escape(str.join)
-
end
-
-
# Formats a +number+ into a currency string (e.g., $13.65). You can customize the format
-
# in the +options+ hash.
-
#
-
# ==== Options
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the level of precision (defaults to 2).
-
# * <tt>:unit</tt> - Sets the denomination of the currency (defaults to "$").
-
# * <tt>:separator</tt> - Sets the separator between the units (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to ",").
-
# * <tt>:format</tt> - Sets the format for non-negative numbers (defaults to "%u%n").
-
# Fields are <tt>%u</tt> for the currency, and <tt>%n</tt>
-
# for the number.
-
# * <tt>:negative_format</tt> - Sets the format for negative numbers (defaults to prepending
-
# an hyphen to the formatted number given by <tt>:format</tt>).
-
# Accepts the same fields than <tt>:format</tt>, except
-
# <tt>%n</tt> is here the absolute value of the number.
-
#
-
# ==== Examples
-
# number_to_currency(1234567890.50) # => $1,234,567,890.50
-
# number_to_currency(1234567890.506) # => $1,234,567,890.51
-
# number_to_currency(1234567890.506, :precision => 3) # => $1,234,567,890.506
-
# number_to_currency(1234567890.506, :locale => :fr) # => 1 234 567 890,506 €
-
#
-
# number_to_currency(-1234567890.50, :negative_format => "(%u%n)")
-
# # => ($1,234,567,890.51)
-
# number_to_currency(1234567890.50, :unit => "£", :separator => ",", :delimiter => "")
-
# # => £1234567890,50
-
# number_to_currency(1234567890.50, :unit => "£", :separator => ",", :delimiter => "", :format => "%n %u")
-
# # => 1234567890,50 £
-
1
def number_to_currency(number, options = {})
-
return unless number
-
-
options.symbolize_keys!
-
-
defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
-
currency = I18n.translate(:'number.currency.format', :locale => options[:locale], :default => {})
-
-
defaults = DEFAULT_CURRENCY_VALUES.merge(defaults).merge!(currency)
-
defaults[:negative_format] = "-" + options[:format] if options[:format]
-
options = defaults.merge!(options)
-
-
unit = options.delete(:unit)
-
format = options.delete(:format)
-
-
if number.to_f < 0
-
format = options.delete(:negative_format)
-
number = number.respond_to?("abs") ? number.abs : number.sub(/^-/, '')
-
end
-
-
begin
-
value = number_with_precision(number, options.merge(:raise => true))
-
format.gsub(/%n/, value).gsub(/%u/, unit).html_safe
-
rescue InvalidNumberError => e
-
if options[:raise]
-
raise
-
else
-
formatted_number = format.gsub(/%n/, e.number).gsub(/%u/, unit)
-
e.number.to_s.html_safe? ? formatted_number.html_safe : formatted_number
-
end
-
end
-
-
end
-
-
# Formats a +number+ as a percentage string (e.g., 65%). You can customize the
-
# format in the +options+ hash.
-
#
-
# ==== Options
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the precision of the number (defaults to 3).
-
# * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+, the # of fractional digits (defaults to +false+)
-
# * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
-
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes insignificant zeros after the decimal separator (defaults to +false+)
-
#
-
# ==== Examples
-
# number_to_percentage(100) # => 100.000%
-
# number_to_percentage(100, :precision => 0) # => 100%
-
# number_to_percentage(1000, :delimiter => '.', :separator => ',') # => 1.000,000%
-
# number_to_percentage(302.24398923423, :precision => 5) # => 302.24399%
-
# number_to_percentage(1000, :locale => :fr) # => 1 000,000%
-
1
def number_to_percentage(number, options = {})
-
return unless number
-
-
options.symbolize_keys!
-
-
defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
-
percentage = I18n.translate(:'number.percentage.format', :locale => options[:locale], :default => {})
-
defaults = defaults.merge(percentage)
-
-
options = options.reverse_merge(defaults)
-
-
begin
-
"#{number_with_precision(number, options.merge(:raise => true))}%".html_safe
-
rescue InvalidNumberError => e
-
if options[:raise]
-
raise
-
else
-
e.number.to_s.html_safe? ? "#{e.number}%".html_safe : "#{e.number}%"
-
end
-
end
-
end
-
-
# Formats a +number+ with grouped thousands using +delimiter+ (e.g., 12,324). You can
-
# customize the format in the +options+ hash.
-
#
-
# ==== Options
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to ",").
-
# * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to ".").
-
#
-
# ==== Examples
-
# number_with_delimiter(12345678) # => 12,345,678
-
# number_with_delimiter(12345678.05) # => 12,345,678.05
-
# number_with_delimiter(12345678, :delimiter => ".") # => 12.345.678
-
# number_with_delimiter(12345678, :separator => ",") # => 12,345,678
-
# number_with_delimiter(12345678.05, :locale => :fr) # => 12 345 678,05
-
# number_with_delimiter(98765432.98, :delimiter => " ", :separator => ",")
-
# # => 98 765 432,98
-
1
def number_with_delimiter(number, options = {})
-
options.symbolize_keys!
-
-
begin
-
Float(number)
-
rescue ArgumentError, TypeError
-
if options[:raise]
-
raise InvalidNumberError, number
-
else
-
return number
-
end
-
end
-
-
defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
-
options = options.reverse_merge(defaults)
-
-
parts = number.to_s.to_str.split('.')
-
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
-
parts.join(options[:separator]).html_safe
-
-
end
-
-
# Formats a +number+ with the specified level of <tt>:precision</tt> (e.g., 112.32 has a precision
-
# of 2 if +:significant+ is +false+, and 5 if +:significant+ is +true+).
-
# You can customize the format in the +options+ hash.
-
#
-
# ==== Options
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the precision of the number (defaults to 3).
-
# * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+, the # of fractional digits (defaults to +false+)
-
# * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
-
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes insignificant zeros after the decimal separator (defaults to +false+)
-
#
-
# ==== Examples
-
# number_with_precision(111.2345) # => 111.235
-
# number_with_precision(111.2345, :precision => 2) # => 111.23
-
# number_with_precision(13, :precision => 5) # => 13.00000
-
# number_with_precision(389.32314, :precision => 0) # => 389
-
# number_with_precision(111.2345, :significant => true) # => 111
-
# number_with_precision(111.2345, :precision => 1, :significant => true) # => 100
-
# number_with_precision(13, :precision => 5, :significant => true) # => 13.000
-
# number_with_precision(111.234, :locale => :fr) # => 111,234
-
# number_with_precision(13, :precision => 5, :significant => true, :strip_insignificant_zeros => true)
-
# # => 13
-
# number_with_precision(389.32314, :precision => 4, :significant => true) # => 389.3
-
# number_with_precision(1111.2345, :precision => 2, :separator => ',', :delimiter => '.')
-
# # => 1.111,23
-
1
def number_with_precision(number, options = {})
-
options.symbolize_keys!
-
-
number = begin
-
Float(number)
-
rescue ArgumentError, TypeError
-
if options[:raise]
-
raise InvalidNumberError, number
-
else
-
return number
-
end
-
end
-
-
defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
-
precision_defaults = I18n.translate(:'number.precision.format', :locale => options[:locale], :default => {})
-
defaults = defaults.merge(precision_defaults)
-
-
options = options.reverse_merge(defaults) # Allow the user to unset default values: Eg.: :significant => false
-
precision = options.delete :precision
-
significant = options.delete :significant
-
strip_insignificant_zeros = options.delete :strip_insignificant_zeros
-
-
if significant and precision > 0
-
if number == 0
-
digits, rounded_number = 1, 0
-
else
-
digits = (Math.log10(number.abs) + 1).floor
-
rounded_number = (BigDecimal.new(number.to_s) / BigDecimal.new((10 ** (digits - precision)).to_f.to_s)).round.to_f * 10 ** (digits - precision)
-
digits = (Math.log10(rounded_number.abs) + 1).floor # After rounding, the number of digits may have changed
-
end
-
precision -= digits
-
precision = precision > 0 ? precision : 0 #don't let it be negative
-
else
-
rounded_number = BigDecimal.new(number.to_s).round(precision).to_f
-
end
-
formatted_number = number_with_delimiter("%01.#{precision}f" % rounded_number, options)
-
if strip_insignificant_zeros
-
escaped_separator = Regexp.escape(options[:separator])
-
formatted_number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '').html_safe
-
else
-
formatted_number
-
end
-
-
end
-
-
1
STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb].freeze
-
-
# Formats the bytes in +number+ into a more understandable representation
-
# (e.g., giving it 1500 yields 1.5 KB). This method is useful for
-
# reporting file sizes to users. You can customize the
-
# format in the +options+ hash.
-
#
-
# See <tt>number_to_human</tt> if you want to pretty-print a generic number.
-
#
-
# ==== Options
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the precision of the number (defaults to 3).
-
# * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+, the # of fractional digits (defaults to +true+)
-
# * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
-
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes insignificant zeros after the decimal separator (defaults to +true+)
-
# * <tt>:prefix</tt> - If +:si+ formats the number using the SI prefix (defaults to :binary)
-
# ==== Examples
-
# number_to_human_size(123) # => 123 Bytes
-
# number_to_human_size(1234) # => 1.21 KB
-
# number_to_human_size(12345) # => 12.1 KB
-
# number_to_human_size(1234567) # => 1.18 MB
-
# number_to_human_size(1234567890) # => 1.15 GB
-
# number_to_human_size(1234567890123) # => 1.12 TB
-
# number_to_human_size(1234567, :precision => 2) # => 1.2 MB
-
# number_to_human_size(483989, :precision => 2) # => 470 KB
-
# number_to_human_size(1234567, :precision => 2, :separator => ',') # => 1,2 MB
-
#
-
# Non-significant zeros after the fractional separator are stripped out by default (set
-
# <tt>:strip_insignificant_zeros</tt> to +false+ to change that):
-
# number_to_human_size(1234567890123, :precision => 5) # => "1.1229 TB"
-
# number_to_human_size(524288000, :precision => 5) # => "500 MB"
-
1
def number_to_human_size(number, options = {})
-
options.symbolize_keys!
-
-
number = begin
-
Float(number)
-
rescue ArgumentError, TypeError
-
if options[:raise]
-
raise InvalidNumberError, number
-
else
-
return number
-
end
-
end
-
-
defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
-
human = I18n.translate(:'number.human.format', :locale => options[:locale], :default => {})
-
defaults = defaults.merge(human)
-
-
options = options.reverse_merge(defaults)
-
#for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
-
options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros)
-
-
storage_units_format = I18n.translate(:'number.human.storage_units.format', :locale => options[:locale], :raise => true)
-
-
base = options[:prefix] == :si ? 1000 : 1024
-
-
if number.to_i < base
-
unit = I18n.translate(:'number.human.storage_units.units.byte', :locale => options[:locale], :count => number.to_i, :raise => true)
-
storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit).html_safe
-
else
-
max_exp = STORAGE_UNITS.size - 1
-
exponent = (Math.log(number) / Math.log(base)).to_i # Convert to base
-
exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
-
number /= base ** exponent
-
-
unit_key = STORAGE_UNITS[exponent]
-
unit = I18n.translate(:"number.human.storage_units.units.#{unit_key}", :locale => options[:locale], :count => number, :raise => true)
-
-
formatted_number = number_with_precision(number, options)
-
storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit).html_safe
-
end
-
end
-
-
1
DECIMAL_UNITS = {0 => :unit, 1 => :ten, 2 => :hundred, 3 => :thousand, 6 => :million, 9 => :billion, 12 => :trillion, 15 => :quadrillion,
-
-1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto}.freeze
-
-
# Pretty prints (formats and approximates) a number in a way it is more readable by humans
-
# (eg.: 1200000000 becomes "1.2 Billion"). This is useful for numbers that
-
# can get very large (and too hard to read).
-
#
-
# See <tt>number_to_human_size</tt> if you want to print a file size.
-
#
-
# You can also define you own unit-quantifier names if you want to use other decimal units
-
# (eg.: 1500 becomes "1.5 kilometers", 0.150 becomes "150 milliliters", etc). You may define
-
# a wide range of unit quantifiers, even fractional ones (centi, deci, mili, etc).
-
#
-
# ==== Options
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the precision of the number (defaults to 3).
-
# * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+, the # of fractional digits (defaults to +true+)
-
# * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
-
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes insignificant zeros after the decimal separator (defaults to +true+)
-
# * <tt>:units</tt> - A Hash of unit quantifier names. Or a string containing an i18n scope where to find this hash. It might have the following keys:
-
# * *integers*: <tt>:unit</tt>, <tt>:ten</tt>, <tt>:hundred</tt>, <tt>:thousand</tt>, <tt>:million</tt>, <tt>:billion</tt>, <tt>:trillion</tt>, <tt>:quadrillion</tt>
-
# * *fractionals*: <tt>:deci</tt>, <tt>:centi</tt>, <tt>:mili</tt>, <tt>:micro</tt>, <tt>:nano</tt>, <tt>:pico</tt>, <tt>:femto</tt>
-
# * <tt>:format</tt> - Sets the format of the output string (defaults to "%n %u"). The field types are:
-
#
-
# %u The quantifier (ex.: 'thousand')
-
# %n The number
-
#
-
# ==== Examples
-
# number_to_human(123) # => "123"
-
# number_to_human(1234) # => "1.23 Thousand"
-
# number_to_human(12345) # => "12.3 Thousand"
-
# number_to_human(1234567) # => "1.23 Million"
-
# number_to_human(1234567890) # => "1.23 Billion"
-
# number_to_human(1234567890123) # => "1.23 Trillion"
-
# number_to_human(1234567890123456) # => "1.23 Quadrillion"
-
# number_to_human(1234567890123456789) # => "1230 Quadrillion"
-
# number_to_human(489939, :precision => 2) # => "490 Thousand"
-
# number_to_human(489939, :precision => 4) # => "489.9 Thousand"
-
# number_to_human(1234567, :precision => 4,
-
# :significant => false) # => "1.2346 Million"
-
# number_to_human(1234567, :precision => 1,
-
# :separator => ',',
-
# :significant => false) # => "1,2 Million"
-
#
-
# Unsignificant zeros after the decimal separator are stripped out by default (set
-
# <tt>:strip_insignificant_zeros</tt> to +false+ to change that):
-
# number_to_human(12345012345, :significant_digits => 6) # => "12.345 Billion"
-
# number_to_human(500000000, :precision => 5) # => "500 Million"
-
#
-
# ==== Custom Unit Quantifiers
-
#
-
# You can also use your own custom unit quantifiers:
-
# number_to_human(500000, :units => {:unit => "ml", :thousand => "lt"}) # => "500 lt"
-
#
-
# If in your I18n locale you have:
-
# distance:
-
# centi:
-
# one: "centimeter"
-
# other: "centimeters"
-
# unit:
-
# one: "meter"
-
# other: "meters"
-
# thousand:
-
# one: "kilometer"
-
# other: "kilometers"
-
# billion: "gazillion-distance"
-
#
-
# Then you could do:
-
#
-
# number_to_human(543934, :units => :distance) # => "544 kilometers"
-
# number_to_human(54393498, :units => :distance) # => "54400 kilometers"
-
# number_to_human(54393498000, :units => :distance) # => "54.4 gazillion-distance"
-
# number_to_human(343, :units => :distance, :precision => 1) # => "300 meters"
-
# number_to_human(1, :units => :distance) # => "1 meter"
-
# number_to_human(0.34, :units => :distance) # => "34 centimeters"
-
#
-
1
def number_to_human(number, options = {})
-
options.symbolize_keys!
-
-
number = begin
-
Float(number)
-
rescue ArgumentError, TypeError
-
if options[:raise]
-
raise InvalidNumberError, number
-
else
-
return number
-
end
-
end
-
-
defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
-
human = I18n.translate(:'number.human.format', :locale => options[:locale], :default => {})
-
defaults = defaults.merge(human)
-
-
options = options.reverse_merge(defaults)
-
#for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
-
options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros)
-
-
inverted_du = DECIMAL_UNITS.invert
-
-
units = options.delete :units
-
unit_exponents = case units
-
when Hash
-
units
-
when String, Symbol
-
I18n.translate(:"#{units}", :locale => options[:locale], :raise => true)
-
when nil
-
I18n.translate(:"number.human.decimal_units.units", :locale => options[:locale], :raise => true)
-
else
-
raise ArgumentError, ":units must be a Hash or String translation scope."
-
end.keys.map{|e_name| inverted_du[e_name] }.sort_by{|e| -e}
-
-
number_exponent = number != 0 ? Math.log10(number.abs).floor : 0
-
display_exponent = unit_exponents.find{ |e| number_exponent >= e } || 0
-
number /= 10 ** display_exponent
-
-
unit = case units
-
when Hash
-
units[DECIMAL_UNITS[display_exponent]]
-
when String, Symbol
-
I18n.translate(:"#{units}.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
-
else
-
I18n.translate(:"number.human.decimal_units.units.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
-
end
-
-
decimal_format = options[:format] || I18n.translate(:'number.human.decimal_units.format', :locale => options[:locale], :default => "%n %u")
-
formatted_number = number_with_precision(number, options)
-
decimal_format.gsub(/%n/, formatted_number).gsub(/%u/, unit).strip.html_safe
-
end
-
-
end
-
end
-
end
-
1
require 'active_support/core_ext/string/output_safety'
-
-
1
module ActionView #:nodoc:
-
# = Action View Raw Output Helper
-
1
module Helpers #:nodoc:
-
1
module OutputSafetyHelper
-
# This method outputs without escaping a string. Since escaping tags is
-
# now default, this can be used when you don't want Rails to automatically
-
# escape tags. This is not recommended if the data is coming from the user's
-
# input.
-
#
-
# For example:
-
#
-
# <%=raw @user.name %>
-
1
def raw(stringish)
-
stringish.to_s.html_safe
-
end
-
-
# This method returns a html safe string similar to what <tt>Array#join</tt>
-
# would return. All items in the array, including the supplied separator, are
-
# html escaped unless they are html safe, and the returned string is marked
-
# as html safe.
-
#
-
# safe_join(["<p>foo</p>".html_safe, "<p>bar</p>"], "<br />")
-
# # => "<p>foo</p><br /><p>bar</p>"
-
#
-
# safe_join(["<p>foo</p>".html_safe, "<p>bar</p>".html_safe], "<br />".html_safe)
-
# # => "<p>foo</p><br /><p>bar</p>"
-
#
-
1
def safe_join(array, sep=$,)
-
sep ||= "".html_safe
-
sep = ERB::Util.html_escape(sep)
-
-
array.map { |i| ERB::Util.html_escape(i) }.join(sep).html_safe
-
end
-
end
-
end
-
end
-
1
require 'action_controller/record_identifier'
-
-
1
module ActionView
-
# = Action View Record Tag Helpers
-
1
module Helpers
-
1
module RecordTagHelper
-
1
include ActionController::RecordIdentifier
-
-
# Produces a wrapper DIV element with id and class parameters that
-
# relate to the specified Active Record object. Usage example:
-
#
-
# <%= div_for(@person, :class => "foo") do %>
-
# <%= @person.name %>
-
# <% end %>
-
#
-
# produces:
-
#
-
# <div id="person_123" class="person foo"> Joe Bloggs </div>
-
#
-
1
def div_for(record, *args, &block)
-
content_tag_for(:div, record, *args, &block)
-
end
-
-
# content_tag_for creates an HTML element with id and class parameters
-
# that relate to the specified Active Record object. For example:
-
#
-
# <%= content_tag_for(:tr, @person) do %>
-
# <td><%= @person.first_name %></td>
-
# <td><%= @person.last_name %></td>
-
# <% end %>
-
#
-
# would produce the following HTML (assuming @person is an instance of
-
# a Person object, with an id value of 123):
-
#
-
# <tr id="person_123" class="person">....</tr>
-
#
-
# If you require the HTML id attribute to have a prefix, you can specify it:
-
#
-
# <%= content_tag_for(:tr, @person, :foo) do %> ...
-
#
-
# produces:
-
#
-
# <tr id="foo_person_123" class="person">...
-
#
-
# content_tag_for also accepts a hash of options, which will be converted to
-
# additional HTML attributes. If you specify a <tt>:class</tt> value, it will be combined
-
# with the default class name for your object. For example:
-
#
-
# <%= content_tag_for(:li, @person, :class => "bar") %>...
-
#
-
# produces:
-
#
-
# <li id="person_123" class="person bar">...
-
#
-
1
def content_tag_for(tag_name, record, prefix = nil, options = nil, &block)
-
options, prefix = prefix, nil if prefix.is_a?(Hash)
-
options ||= {}
-
options.merge!({ :class => "#{dom_class(record, prefix)} #{options[:class]}".strip, :id => dom_id(record, prefix) })
-
content_tag(tag_name, options, &block)
-
end
-
end
-
end
-
end
-
1
module ActionView
-
1
module Helpers
-
# = Action View Rendering
-
#
-
# Implements methods that allow rendering from a view context.
-
# In order to use this module, all you need is to implement
-
# view_renderer that returns an ActionView::Renderer object.
-
1
module RenderingHelper
-
# Returns the result of a render that's dictated by the options hash. The primary options are:
-
#
-
# * <tt>:partial</tt> - See ActionView::Partials.
-
# * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add :locals to pass in those.
-
# * <tt>:inline</tt> - Renders an inline template similar to how it's done in the controller.
-
# * <tt>:text</tt> - Renders the text passed in out.
-
#
-
# If no options hash is passed or :update specified, the default is to render a partial and use the second parameter
-
# as the locals hash.
-
1
def render(options = {}, locals = {}, &block)
-
case options
-
when Hash
-
if block_given?
-
view_renderer.render_partial(self, options.merge(:partial => options[:layout]), &block)
-
else
-
view_renderer.render(self, options)
-
end
-
else
-
view_renderer.render_partial(self, :partial => options, :locals => locals)
-
end
-
end
-
-
# Overwrites _layout_for in the context object so it supports the case a block is
-
# passed to a partial. Returns the contents that are yielded to a layout, given a
-
# name or a block.
-
#
-
# You can think of a layout as a method that is called with a block. If the user calls
-
# <tt>yield :some_name</tt>, the block, by default, returns <tt>content_for(:some_name)</tt>.
-
# If the user calls simply +yield+, the default block returns <tt>content_for(:layout)</tt>.
-
#
-
# The user can override this default by passing a block to the layout:
-
#
-
# # The template
-
# <%= render :layout => "my_layout" do %>
-
# Content
-
# <% end %>
-
#
-
# # The layout
-
# <html>
-
# <%= yield %>
-
# </html>
-
#
-
# In this case, instead of the default block, which would return <tt>content_for(:layout)</tt>,
-
# this method returns the block that was passed in to <tt>render :layout</tt>, and the response
-
# would be
-
#
-
# <html>
-
# Content
-
# </html>
-
#
-
# Finally, the block can take block arguments, which can be passed in by +yield+:
-
#
-
# # The template
-
# <%= render :layout => "my_layout" do |customer| %>
-
# Hello <%= customer.name %>
-
# <% end %>
-
#
-
# # The layout
-
# <html>
-
# <%= yield Struct.new(:name).new("David") %>
-
# </html>
-
#
-
# In this case, the layout would receive the block passed into <tt>render :layout</tt>,
-
# and the struct specified would be passed into the block as an argument. The result
-
# would be
-
#
-
# <html>
-
# Hello David
-
# </html>
-
#
-
1
def _layout_for(*args, &block)
-
name = args.first
-
-
if block && !name.is_a?(Symbol)
-
capture(*args, &block)
-
else
-
super
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/try'
-
1
require 'action_controller/vendor/html-scanner'
-
1
require 'action_view/helpers/tag_helper'
-
-
1
module ActionView
-
# = Action View Sanitize Helpers
-
1
module Helpers #:nodoc:
-
# The SanitizeHelper module provides a set of methods for scrubbing text of undesired HTML elements.
-
# These helper methods extend Action View making them callable within your template files.
-
1
module SanitizeHelper
-
1
extend ActiveSupport::Concern
-
# This +sanitize+ helper will html encode all tags and strip all attributes that
-
# aren't specifically allowed.
-
#
-
# It also strips href/src tags with invalid protocols, like javascript: especially.
-
# It does its best to counter any tricks that hackers may use, like throwing in
-
# unicode/ascii/hex values to get past the javascript: filters. Check out
-
# the extensive test suite.
-
#
-
# <%= sanitize @article.body %>
-
#
-
# You can add or remove tags/attributes if you want to customize it a bit.
-
# See ActionView::Base for full docs on the available options. You can add
-
# tags/attributes for single uses of +sanitize+ by passing either the
-
# <tt>:attributes</tt> or <tt>:tags</tt> options:
-
#
-
# Normal Use
-
#
-
# <%= sanitize @article.body %>
-
#
-
# Custom Use (only the mentioned tags and attributes are allowed, nothing else)
-
#
-
# <%= sanitize @article.body, :tags => %w(table tr td), :attributes => %w(id class style) %>
-
#
-
# Add table tags to the default allowed tags
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_allowed_tags = 'table', 'tr', 'td'
-
# end
-
#
-
# Remove tags to the default allowed tags
-
#
-
# class Application < Rails::Application
-
# config.after_initialize do
-
# ActionView::Base.sanitized_allowed_tags.delete 'div'
-
# end
-
# end
-
#
-
# Change allowed default attributes
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_allowed_attributes = 'id', 'class', 'style'
-
# end
-
#
-
# Please note that sanitizing user-provided text does not guarantee that the
-
# resulting markup is valid (conforming to a document type) or even well-formed.
-
# The output may still contain e.g. unescaped '<', '>', '&' characters and
-
# confuse browsers.
-
#
-
1
def sanitize(html, options = {})
-
self.class.white_list_sanitizer.sanitize(html, options).try(:html_safe)
-
end
-
-
# Sanitizes a block of CSS code. Used by +sanitize+ when it comes across a style attribute.
-
1
def sanitize_css(style)
-
self.class.white_list_sanitizer.sanitize_css(style)
-
end
-
-
# Strips all HTML tags from the +html+, including comments. This uses the
-
# html-scanner tokenizer and so its HTML parsing ability is limited by
-
# that of html-scanner.
-
#
-
# ==== Examples
-
#
-
# strip_tags("Strip <i>these</i> tags!")
-
# # => Strip these tags!
-
#
-
# strip_tags("<b>Bold</b> no more! <a href='more.html'>See more here</a>...")
-
# # => Bold no more! See more here...
-
#
-
# strip_tags("<div id='top-bar'>Welcome to my website!</div>")
-
# # => Welcome to my website!
-
1
def strip_tags(html)
-
self.class.full_sanitizer.sanitize(html).try(:html_safe)
-
end
-
-
# Strips all link tags from +text+ leaving just the link text.
-
#
-
# ==== Examples
-
# strip_links('<a href="http://www.rubyonrails.org">Ruby on Rails</a>')
-
# # => Ruby on Rails
-
#
-
# strip_links('Please e-mail me at <a href="mailto:me@email.com">me@email.com</a>.')
-
# # => Please e-mail me at me@email.com.
-
#
-
# strip_links('Blog: <a href="http://www.myblog.com/" class="nav" target=\"_blank\">Visit</a>.')
-
# # => Blog: Visit.
-
1
def strip_links(html)
-
self.class.link_sanitizer.sanitize(html)
-
end
-
-
1
module ClassMethods #:nodoc:
-
1
attr_writer :full_sanitizer, :link_sanitizer, :white_list_sanitizer
-
-
1
def sanitized_protocol_separator
-
white_list_sanitizer.protocol_separator
-
end
-
-
1
def sanitized_uri_attributes
-
white_list_sanitizer.uri_attributes
-
end
-
-
1
def sanitized_bad_tags
-
white_list_sanitizer.bad_tags
-
end
-
-
1
def sanitized_allowed_tags
-
white_list_sanitizer.allowed_tags
-
end
-
-
1
def sanitized_allowed_attributes
-
white_list_sanitizer.allowed_attributes
-
end
-
-
1
def sanitized_allowed_css_properties
-
white_list_sanitizer.allowed_css_properties
-
end
-
-
1
def sanitized_allowed_css_keywords
-
white_list_sanitizer.allowed_css_keywords
-
end
-
-
1
def sanitized_shorthand_css_properties
-
white_list_sanitizer.shorthand_css_properties
-
end
-
-
1
def sanitized_allowed_protocols
-
white_list_sanitizer.allowed_protocols
-
end
-
-
1
def sanitized_protocol_separator=(value)
-
white_list_sanitizer.protocol_separator = value
-
end
-
-
# Gets the HTML::FullSanitizer instance used by +strip_tags+. Replace with
-
# any object that responds to +sanitize+.
-
#
-
# class Application < Rails::Application
-
# config.action_view.full_sanitizer = MySpecialSanitizer.new
-
# end
-
#
-
1
def full_sanitizer
-
@full_sanitizer ||= HTML::FullSanitizer.new
-
end
-
-
# Gets the HTML::LinkSanitizer instance used by +strip_links+. Replace with
-
# any object that responds to +sanitize+.
-
#
-
# class Application < Rails::Application
-
# config.action_view.link_sanitizer = MySpecialSanitizer.new
-
# end
-
#
-
1
def link_sanitizer
-
@link_sanitizer ||= HTML::LinkSanitizer.new
-
end
-
-
# Gets the HTML::WhiteListSanitizer instance used by sanitize and +sanitize_css+.
-
# Replace with any object that responds to +sanitize+.
-
#
-
# class Application < Rails::Application
-
# config.action_view.white_list_sanitizer = MySpecialSanitizer.new
-
# end
-
#
-
1
def white_list_sanitizer
-
@white_list_sanitizer ||= HTML::WhiteListSanitizer.new
-
end
-
-
# Adds valid HTML attributes that the +sanitize+ helper checks for URIs.
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_uri_attributes = 'lowsrc', 'target'
-
# end
-
#
-
1
def sanitized_uri_attributes=(attributes)
-
HTML::WhiteListSanitizer.uri_attributes.merge(attributes)
-
end
-
-
# Adds to the Set of 'bad' tags for the +sanitize+ helper.
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_bad_tags = 'embed', 'object'
-
# end
-
#
-
1
def sanitized_bad_tags=(attributes)
-
HTML::WhiteListSanitizer.bad_tags.merge(attributes)
-
end
-
-
# Adds to the Set of allowed tags for the +sanitize+ helper.
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_allowed_tags = 'table', 'tr', 'td'
-
# end
-
#
-
1
def sanitized_allowed_tags=(attributes)
-
HTML::WhiteListSanitizer.allowed_tags.merge(attributes)
-
end
-
-
# Adds to the Set of allowed HTML attributes for the +sanitize+ helper.
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_allowed_attributes = 'onclick', 'longdesc'
-
# end
-
#
-
1
def sanitized_allowed_attributes=(attributes)
-
HTML::WhiteListSanitizer.allowed_attributes.merge(attributes)
-
end
-
-
# Adds to the Set of allowed CSS properties for the #sanitize and +sanitize_css+ helpers.
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_allowed_css_properties = 'expression'
-
# end
-
#
-
1
def sanitized_allowed_css_properties=(attributes)
-
HTML::WhiteListSanitizer.allowed_css_properties.merge(attributes)
-
end
-
-
# Adds to the Set of allowed CSS keywords for the +sanitize+ and +sanitize_css+ helpers.
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_allowed_css_keywords = 'expression'
-
# end
-
#
-
1
def sanitized_allowed_css_keywords=(attributes)
-
HTML::WhiteListSanitizer.allowed_css_keywords.merge(attributes)
-
end
-
-
# Adds to the Set of allowed shorthand CSS properties for the +sanitize+ and +sanitize_css+ helpers.
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_shorthand_css_properties = 'expression'
-
# end
-
#
-
1
def sanitized_shorthand_css_properties=(attributes)
-
HTML::WhiteListSanitizer.shorthand_css_properties.merge(attributes)
-
end
-
-
# Adds to the Set of allowed protocols for the +sanitize+ helper.
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_allowed_protocols = 'ssh', 'feed'
-
# end
-
#
-
1
def sanitized_allowed_protocols=(attributes)
-
HTML::WhiteListSanitizer.allowed_protocols.merge(attributes)
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/string/output_safety'
-
1
require 'set'
-
-
1
module ActionView
-
# = Action View Tag Helpers
-
1
module Helpers #:nodoc:
-
# Provides methods to generate HTML tags programmatically when you can't use
-
# a Builder. By default, they output XHTML compliant tags.
-
1
module TagHelper
-
1
extend ActiveSupport::Concern
-
1
include CaptureHelper
-
-
1
BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked autobuffer
-
autoplay controls loop selected hidden scoped async
-
defer reversed ismap seemless muted required
-
autofocus novalidate formnovalidate open pubdate).to_set
-
24
BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map {|attribute| attribute.to_sym })
-
-
# Returns an empty HTML tag of type +name+ which by default is XHTML
-
# compliant. Set +open+ to true to create an open tag compatible
-
# with HTML 4.0 and below. Add HTML attributes by passing an attributes
-
# hash to +options+. Set +escape+ to false to disable attribute value
-
# escaping.
-
#
-
# ==== Options
-
# You can use symbols or strings for the attribute names.
-
#
-
# Use +true+ with boolean attributes that can render with no value, like
-
# +disabled+ and +readonly+.
-
#
-
# HTML5 <tt>data-*</tt> attributes can be set with a single +data+ key
-
# pointing to a hash of sub-attributes.
-
#
-
# To play nicely with JavaScript conventions sub-attributes are dasherized.
-
# For example, a key +user_id+ would render as <tt>data-user-id</tt> and
-
# thus accessed as <tt>dataset.userId</tt>.
-
#
-
# Values are encoded to JSON, with the exception of strings and symbols.
-
# This may come in handy when using jQuery's HTML5-aware <tt>.data()<tt>
-
# from 1.4.3.
-
#
-
# ==== Examples
-
# tag("br")
-
# # => <br />
-
#
-
# tag("br", nil, true)
-
# # => <br>
-
#
-
# tag("input", :type => 'text', :disabled => true)
-
# # => <input type="text" disabled="disabled" />
-
#
-
# tag("img", :src => "open & shut.png")
-
# # => <img src="open & shut.png" />
-
#
-
# tag("img", {:src => "open & shut.png"}, false, false)
-
# # => <img src="open & shut.png" />
-
#
-
# tag("div", :data => {:name => 'Stephen', :city_state => %w(Chicago IL)})
-
# # => <div data-name="Stephen" data-city-state="["Chicago","IL"]" />
-
1
def tag(name, options = nil, open = false, escape = true)
-
"<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe
-
end
-
-
# Returns an HTML block tag of type +name+ surrounding the +content+. Add
-
# HTML attributes by passing an attributes hash to +options+.
-
# Instead of passing the content as an argument, you can also use a block
-
# in which case, you pass your +options+ as the second parameter.
-
# Set escape to false to disable attribute value escaping.
-
#
-
# ==== Options
-
# The +options+ hash is used with attributes with no value like (<tt>disabled</tt> and
-
# <tt>readonly</tt>), which you can give a value of true in the +options+ hash. You can use
-
# symbols or strings for the attribute names.
-
#
-
# ==== Examples
-
# content_tag(:p, "Hello world!")
-
# # => <p>Hello world!</p>
-
# content_tag(:div, content_tag(:p, "Hello world!"), :class => "strong")
-
# # => <div class="strong"><p>Hello world!</p></div>
-
# content_tag("select", options, :multiple => true)
-
# # => <select multiple="multiple">...options...</select>
-
#
-
# <%= content_tag :div, :class => "strong" do -%>
-
# Hello world!
-
# <% end -%>
-
# # => <div class="strong">Hello world!</div>
-
1
def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
-
if block_given?
-
options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
-
content_tag_string(name, capture(&block), options, escape)
-
else
-
content_tag_string(name, content_or_options_with_block, options, escape)
-
end
-
end
-
-
# Returns a CDATA section with the given +content+. CDATA sections
-
# are used to escape blocks of text containing characters which would
-
# otherwise be recognized as markup. CDATA sections begin with the string
-
# <tt><![CDATA[</tt> and end with (and may not contain) the string <tt>]]></tt>.
-
#
-
# ==== Examples
-
# cdata_section("<hello world>")
-
# # => <![CDATA[<hello world>]]>
-
#
-
# cdata_section(File.read("hello_world.txt"))
-
# # => <![CDATA[<hello from a text file]]>
-
1
def cdata_section(content)
-
"<![CDATA[#{content}]]>".html_safe
-
end
-
-
# Returns an escaped version of +html+ without affecting existing escaped entities.
-
#
-
# ==== Examples
-
# escape_once("1 < 2 & 3")
-
# # => "1 < 2 & 3"
-
#
-
# escape_once("<< Accept & Checkout")
-
# # => "<< Accept & Checkout"
-
1
def escape_once(html)
-
ActiveSupport::Multibyte.clean(html.to_s).gsub(/[\"><]|&(?!([a-zA-Z]+|(#\d+));)/) { |special| ERB::Util::HTML_ESCAPE[special] }
-
end
-
-
1
private
-
-
1
def content_tag_string(name, content, options, escape = true)
-
tag_options = tag_options(options, escape) if options
-
"<#{name}#{tag_options}>#{escape ? ERB::Util.h(content) : content}</#{name}>".html_safe
-
end
-
-
1
def tag_options(options, escape = true)
-
unless options.blank?
-
attrs = []
-
options.each_pair do |key, value|
-
if key.to_s == 'data' && value.is_a?(Hash)
-
value.each do |k, v|
-
if !v.is_a?(String) && !v.is_a?(Symbol)
-
v = v.to_json
-
end
-
v = ERB::Util.html_escape(v) if escape
-
attrs << %(data-#{k.to_s.dasherize}="#{v}")
-
end
-
elsif BOOLEAN_ATTRIBUTES.include?(key)
-
attrs << %(#{key}="#{key}") if value
-
elsif !value.nil?
-
final_value = value.is_a?(Array) ? value.join(" ") : value
-
final_value = ERB::Util.html_escape(final_value) if escape
-
attrs << %(#{key}="#{final_value}")
-
end
-
end
-
" #{attrs.sort * ' '}".html_safe unless attrs.empty?
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/string/filters'
-
1
require 'action_view/helpers/tag_helper'
-
-
1
module ActionView
-
# = Action View Text Helpers
-
1
module Helpers #:nodoc:
-
# The TextHelper module provides a set of methods for filtering, formatting
-
# and transforming strings, which can reduce the amount of inline Ruby code in
-
# your views. These helper methods extend Action View making them callable
-
# within your template files.
-
#
-
# ==== Sanitization
-
#
-
# Most text helpers by default sanitize the given content, but do not escape it.
-
# This means HTML tags will appear in the page but all malicious code will be removed.
-
# Let's look at some examples using the +simple_format+ method:
-
#
-
# simple_format('<a href="http://example.com/">Example</a>')
-
# # => "<p><a href=\"http://example.com/\">Example</a></p>"
-
#
-
# simple_format('<a href="javascript:alert(\'no!\')">Example</a>')
-
# # => "<p><a>Example</a></p>"
-
#
-
# If you want to escape all content, you should invoke the +h+ method before
-
# calling the text helper.
-
#
-
# simple_format h('<a href="http://example.com/">Example</a>')
-
# # => "<p><a href=\"http://example.com/\">Example</a></p>"
-
1
module TextHelper
-
1
extend ActiveSupport::Concern
-
-
1
include SanitizeHelper
-
# The preferred method of outputting text in your views is to use the
-
# <%= "text" %> eRuby syntax. The regular _puts_ and _print_ methods
-
# do not operate as expected in an eRuby code block. If you absolutely must
-
# output text within a non-output code block (i.e., <% %>), you can use the concat method.
-
#
-
# ==== Examples
-
# <%
-
# concat "hello"
-
# # is the equivalent of <%= "hello" %>
-
#
-
# if logged_in
-
# concat "Logged in!"
-
# else
-
# concat link_to('login', :action => login)
-
# end
-
# # will either display "Logged in!" or a login link
-
# %>
-
1
def concat(string)
-
output_buffer << string
-
end
-
-
1
def safe_concat(string)
-
output_buffer.respond_to?(:safe_concat) ? output_buffer.safe_concat(string) : concat(string)
-
end
-
-
# Truncates a given +text+ after a given <tt>:length</tt> if +text+ is longer than <tt>:length</tt>
-
# (defaults to 30). The last characters will be replaced with the <tt>:omission</tt> (defaults to "...")
-
# for a total length not exceeding <tt>:length</tt>.
-
#
-
# Pass a <tt>:separator</tt> to truncate +text+ at a natural break.
-
#
-
# The result is not marked as HTML-safe, so will be subject to the default escaping when
-
# used in views, unless wrapped by <tt>raw()</tt>. Care should be taken if +text+ contains HTML tags
-
# or entities, because truncation may produce invalid HTML (such as unbalanced or incomplete tags).
-
#
-
# ==== Examples
-
#
-
# truncate("Once upon a time in a world far far away")
-
# # => "Once upon a time in a world..."
-
#
-
# truncate("Once upon a time in a world far far away", :length => 17)
-
# # => "Once upon a ti..."
-
#
-
# truncate("Once upon a time in a world far far away", :length => 17, :separator => ' ')
-
# # => "Once upon a..."
-
#
-
# truncate("And they found that many people were sleeping better.", :length => 25, :omission => '... (continued)')
-
# # => "And they f... (continued)"
-
#
-
# truncate("<p>Once upon a time in a world far far away</p>")
-
# # => "<p>Once upon a time in a wo..."
-
1
def truncate(text, options = {})
-
options.reverse_merge!(:length => 30)
-
text.truncate(options.delete(:length), options) if text
-
end
-
-
# Highlights one or more +phrases+ everywhere in +text+ by inserting it into
-
# a <tt>:highlighter</tt> string. The highlighter can be specialized by passing <tt>:highlighter</tt>
-
# as a single-quoted string with \1 where the phrase is to be inserted (defaults to
-
# '<strong class="highlight">\1</strong>')
-
#
-
# ==== Examples
-
# highlight('You searched for: rails', 'rails')
-
# # => You searched for: <strong class="highlight">rails</strong>
-
#
-
# highlight('You searched for: ruby, rails, dhh', 'actionpack')
-
# # => You searched for: ruby, rails, dhh
-
#
-
# highlight('You searched for: rails', ['for', 'rails'], :highlighter => '<em>\1</em>')
-
# # => You searched <em>for</em>: <em>rails</em>
-
#
-
# highlight('You searched for: rails', 'rails', :highlighter => '<a href="search?q=\1">\1</a>')
-
# # => You searched for: <a href="search?q=rails">rails</a>
-
#
-
# You can still use <tt>highlight</tt> with the old API that accepts the
-
# +highlighter+ as its optional third parameter:
-
# highlight('You searched for: rails', 'rails', '<a href="search?q=\1">\1</a>') # => You searched for: <a href="search?q=rails">rails</a>
-
1
def highlight(text, phrases, *args)
-
options = args.extract_options!
-
unless args.empty?
-
options[:highlighter] = args[0] || '<strong class="highlight">\1</strong>'
-
end
-
options.reverse_merge!(:highlighter => '<strong class="highlight">\1</strong>')
-
-
text = sanitize(text) unless options[:sanitize] == false
-
if text.blank? || phrases.blank?
-
text
-
else
-
match = Array(phrases).map { |p| Regexp.escape(p) }.join('|')
-
text.gsub(/(#{match})(?!(?:[^<]*?)(?:["'])[^<>]*>)/i, options[:highlighter])
-
end.html_safe
-
end
-
-
# Extracts an excerpt from +text+ that matches the first instance of +phrase+.
-
# The <tt>:radius</tt> option expands the excerpt on each side of the first occurrence of +phrase+ by the number of characters
-
# defined in <tt>:radius</tt> (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+,
-
# then the <tt>:omission</tt> option (which defaults to "...") will be prepended/appended accordingly. The resulting string
-
# will be stripped in any case. If the +phrase+ isn't found, nil is returned.
-
#
-
# ==== Examples
-
# excerpt('This is an example', 'an', :radius => 5)
-
# # => ...s is an exam...
-
#
-
# excerpt('This is an example', 'is', :radius => 5)
-
# # => This is a...
-
#
-
# excerpt('This is an example', 'is')
-
# # => This is an example
-
#
-
# excerpt('This next thing is an example', 'ex', :radius => 2)
-
# # => ...next...
-
#
-
# excerpt('This is also an example', 'an', :radius => 8, :omission => '<chop> ')
-
# # => <chop> is also an example
-
#
-
# You can still use <tt>excerpt</tt> with the old API that accepts the
-
# +radius+ as its optional third and the +ellipsis+ as its
-
# optional forth parameter:
-
# excerpt('This is an example', 'an', 5) # => ...s is an exam...
-
# excerpt('This is also an example', 'an', 8, '<chop> ') # => <chop> is also an example
-
1
def excerpt(text, phrase, *args)
-
return unless text && phrase
-
-
options = args.extract_options!
-
unless args.empty?
-
options[:radius] = args[0] || 100
-
options[:omission] = args[1] || "..."
-
end
-
options.reverse_merge!(:radius => 100, :omission => "...")
-
-
phrase = Regexp.escape(phrase)
-
return unless found_pos = text.mb_chars =~ /(#{phrase})/i
-
-
start_pos = [ found_pos - options[:radius], 0 ].max
-
end_pos = [ [ found_pos + phrase.mb_chars.length + options[:radius] - 1, 0].max, text.mb_chars.length ].min
-
-
prefix = start_pos > 0 ? options[:omission] : ""
-
postfix = end_pos < text.mb_chars.length - 1 ? options[:omission] : ""
-
-
prefix + text.mb_chars[start_pos..end_pos].strip + postfix
-
end
-
-
# Attempts to pluralize the +singular+ word unless +count+ is 1. If
-
# +plural+ is supplied, it will use that when count is > 1, otherwise
-
# it will use the Inflector to determine the plural form
-
#
-
# ==== Examples
-
# pluralize(1, 'person')
-
# # => 1 person
-
#
-
# pluralize(2, 'person')
-
# # => 2 people
-
#
-
# pluralize(3, 'person', 'users')
-
# # => 3 users
-
#
-
# pluralize(0, 'person')
-
# # => 0 people
-
1
def pluralize(count, singular, plural = nil)
-
"#{count || 0} " + ((count == 1 || count =~ /^1(\.0+)?$/) ? singular : (plural || singular.pluralize))
-
end
-
-
# Wraps the +text+ into lines no longer than +line_width+ width. This method
-
# breaks on the first whitespace character that does not exceed +line_width+
-
# (which is 80 by default).
-
#
-
# ==== Examples
-
#
-
# word_wrap('Once upon a time')
-
# # => Once upon a time
-
#
-
# word_wrap('Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding a successor to the throne turned out to be more trouble than anyone could have imagined...')
-
# # => Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding\n a successor to the throne turned out to be more trouble than anyone could have\n imagined...
-
#
-
# word_wrap('Once upon a time', :line_width => 8)
-
# # => Once upon\na time
-
#
-
# word_wrap('Once upon a time', :line_width => 1)
-
# # => Once\nupon\na\ntime
-
#
-
# You can still use <tt>word_wrap</tt> with the old API that accepts the
-
# +line_width+ as its optional second parameter:
-
# word_wrap('Once upon a time', 8) # => Once upon\na time
-
1
def word_wrap(text, *args)
-
options = args.extract_options!
-
unless args.blank?
-
options[:line_width] = args[0] || 80
-
end
-
options.reverse_merge!(:line_width => 80)
-
-
text.split("\n").collect do |line|
-
line.length > options[:line_width] ? line.gsub(/(.{1,#{options[:line_width]}})(\s+|$)/, "\\1\n").strip : line
-
end * "\n"
-
end
-
-
# Returns +text+ transformed into HTML using simple formatting rules.
-
# Two or more consecutive newlines(<tt>\n\n</tt>) are considered as a
-
# paragraph and wrapped in <tt><p></tt> tags. One newline (<tt>\n</tt>) is
-
# considered as a linebreak and a <tt><br /></tt> tag is appended. This
-
# method does not remove the newlines from the +text+.
-
#
-
# You can pass any HTML attributes into <tt>html_options</tt>. These
-
# will be added to all created paragraphs.
-
#
-
# ==== Options
-
# * <tt>:sanitize</tt> - If +false+, does not sanitize +text+.
-
#
-
# ==== Examples
-
# my_text = "Here is some basic text...\n...with a line break."
-
#
-
# simple_format(my_text)
-
# # => "<p>Here is some basic text...\n<br />...with a line break.</p>"
-
#
-
# more_text = "We want to put a paragraph...\n\n...right there."
-
#
-
# simple_format(more_text)
-
# # => "<p>We want to put a paragraph...</p>\n\n<p>...right there.</p>"
-
#
-
# simple_format("Look ma! A class!", :class => 'description')
-
# # => "<p class='description'>Look ma! A class!</p>"
-
#
-
# simple_format("<span>I'm allowed!</span> It's true.", {}, :sanitize => false)
-
# # => "<p><span>I'm allowed!</span> It's true.</p>"
-
1
def simple_format(text, html_options={}, options={})
-
text = '' if text.nil?
-
start_tag = tag('p', html_options, true)
-
text = sanitize(text) unless options[:sanitize] == false
-
text = text.to_str
-
text.gsub!(/\r\n?/, "\n") # \r\n and \r -> \n
-
text.gsub!(/\n\n+/, "</p>\n\n#{start_tag}") # 2+ newline -> paragraph
-
text.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
-
text.insert 0, start_tag
-
text.html_safe.safe_concat("</p>")
-
end
-
-
# Creates a Cycle object whose _to_s_ method cycles through elements of an
-
# array every time it is called. This can be used for example, to alternate
-
# classes for table rows. You can use named cycles to allow nesting in loops.
-
# Passing a Hash as the last parameter with a <tt>:name</tt> key will create a
-
# named cycle. The default name for a cycle without a +:name+ key is
-
# <tt>"default"</tt>. You can manually reset a cycle by calling reset_cycle
-
# and passing the name of the cycle. The current cycle string can be obtained
-
# anytime using the current_cycle method.
-
#
-
# ==== Examples
-
# # Alternate CSS classes for even and odd numbers...
-
# @items = [1,2,3,4]
-
# <table>
-
# <% @items.each do |item| %>
-
# <tr class="<%= cycle("odd", "even") -%>">
-
# <td>item</td>
-
# </tr>
-
# <% end %>
-
# </table>
-
#
-
#
-
# # Cycle CSS classes for rows, and text colors for values within each row
-
# @items = x = [{:first => 'Robert', :middle => 'Daniel', :last => 'James'},
-
# {:first => 'Emily', :middle => 'Shannon', :maiden => 'Pike', :last => 'Hicks'},
-
# {:first => 'June', :middle => 'Dae', :last => 'Jones'}]
-
# <% @items.each do |item| %>
-
# <tr class="<%= cycle("odd", "even", :name => "row_class") -%>">
-
# <td>
-
# <% item.values.each do |value| %>
-
# <%# Create a named cycle "colors" %>
-
# <span style="color:<%= cycle("red", "green", "blue", :name => "colors") -%>">
-
# <%= value %>
-
# </span>
-
# <% end %>
-
# <% reset_cycle("colors") %>
-
# </td>
-
# </tr>
-
# <% end %>
-
1
def cycle(first_value, *values)
-
if (values.last.instance_of? Hash)
-
params = values.pop
-
name = params[:name]
-
else
-
name = "default"
-
end
-
values.unshift(first_value)
-
-
cycle = get_cycle(name)
-
unless cycle && cycle.values == values
-
cycle = set_cycle(name, Cycle.new(*values))
-
end
-
cycle.to_s
-
end
-
-
# Returns the current cycle string after a cycle has been started. Useful
-
# for complex table highlighting or any other design need which requires
-
# the current cycle string in more than one place.
-
#
-
# ==== Example
-
# # Alternate background colors
-
# @items = [1,2,3,4]
-
# <% @items.each do |item| %>
-
# <div style="background-color:<%= cycle("red","white","blue") %>">
-
# <span style="background-color:<%= current_cycle %>"><%= item %></span>
-
# </div>
-
# <% end %>
-
1
def current_cycle(name = "default")
-
cycle = get_cycle(name)
-
cycle.current_value if cycle
-
end
-
-
# Resets a cycle so that it starts from the first element the next time
-
# it is called. Pass in +name+ to reset a named cycle.
-
#
-
# ==== Example
-
# # Alternate CSS classes for even and odd numbers...
-
# @items = [[1,2,3,4], [5,6,3], [3,4,5,6,7,4]]
-
# <table>
-
# <% @items.each do |item| %>
-
# <tr class="<%= cycle("even", "odd") -%>">
-
# <% item.each do |value| %>
-
# <span style="color:<%= cycle("#333", "#666", "#999", :name => "colors") -%>">
-
# <%= value %>
-
# </span>
-
# <% end %>
-
#
-
# <% reset_cycle("colors") %>
-
# </tr>
-
# <% end %>
-
# </table>
-
1
def reset_cycle(name = "default")
-
cycle = get_cycle(name)
-
cycle.reset if cycle
-
end
-
-
1
class Cycle #:nodoc:
-
1
attr_reader :values
-
-
1
def initialize(first_value, *values)
-
@values = values.unshift(first_value)
-
reset
-
end
-
-
1
def reset
-
@index = 0
-
end
-
-
1
def current_value
-
@values[previous_index].to_s
-
end
-
-
1
def to_s
-
value = @values[@index].to_s
-
@index = next_index
-
return value
-
end
-
-
1
private
-
-
1
def next_index
-
step_index(1)
-
end
-
-
1
def previous_index
-
step_index(-1)
-
end
-
-
1
def step_index(n)
-
(@index + n) % @values.size
-
end
-
end
-
-
1
private
-
# The cycle helpers need to store the cycles in a place that is
-
# guaranteed to be reset every time a page is rendered, so it
-
# uses an instance variable of ActionView::Base.
-
1
def get_cycle(name)
-
@_cycles = Hash.new unless defined?(@_cycles)
-
return @_cycles[name]
-
end
-
-
1
def set_cycle(name, cycle_object)
-
@_cycles = Hash.new unless defined?(@_cycles)
-
@_cycles[name] = cycle_object
-
end
-
end
-
end
-
end
-
1
require 'action_view/helpers/tag_helper'
-
1
require 'i18n/exceptions'
-
-
1
module I18n
-
1
class ExceptionHandler
-
include Module.new {
-
1
def call(exception, locale, key, options)
-
exception.is_a?(MissingTranslation) && options[:rescue_format] == :html ? super.html_safe : super
-
end
-
1
}
-
end
-
end
-
-
1
module ActionView
-
# = Action View Translation Helpers
-
1
module Helpers
-
1
module TranslationHelper
-
# Delegates to <tt>I18n#translate</tt> but also performs three additional functions.
-
#
-
# First, it'll pass the <tt>:rescue_format => :html</tt> option to I18n so that any
-
# thrown +MissingTranslation+ messages will be turned into inline spans that
-
#
-
# * have a "translation-missing" class set,
-
# * contain the missing key as a title attribute and
-
# * a titleized version of the last key segment as a text.
-
#
-
# E.g. the value returned for a missing translation key :"blog.post.title" will be
-
# <span class="translation_missing" title="translation missing: en.blog.post.title">Title</span>.
-
# This way your views will display rather reasonable strings but it will still
-
# be easy to spot missing translations.
-
#
-
# Second, it'll scope the key by the current partial if the key starts
-
# with a period. So if you call <tt>translate(".foo")</tt> from the
-
# <tt>people/index.html.erb</tt> template, you'll actually be calling
-
# <tt>I18n.translate("people.index.foo")</tt>. This makes it less repetitive
-
# to translate many keys within the same partials and gives you a simple framework
-
# for scoping them consistently. If you don't prepend the key with a period,
-
# nothing is converted.
-
#
-
# Third, it'll mark the translation as safe HTML if the key has the suffix
-
# "_html" or the last element of the key is the word "html". For example,
-
# calling translate("footer_html") or translate("footer.html") will return
-
# a safe HTML string that won't be escaped by other HTML helper methods. This
-
# naming convention helps to identify translations that include HTML tags so that
-
# you know what kind of output to expect when you call translate in a template.
-
1
def translate(key, options = {})
-
options.merge!(:rescue_format => :html) unless options.key?(:rescue_format)
-
translation = I18n.translate(scope_key_by_partial(key), options)
-
if html_safe_translation_key?(key) && translation.respond_to?(:html_safe)
-
translation.html_safe
-
else
-
translation
-
end
-
end
-
1
alias :t :translate
-
-
# Delegates to <tt>I18n.localize</tt> with no additional functionality.
-
1
def localize(*args)
-
I18n.localize(*args)
-
end
-
1
alias :l :localize
-
-
1
private
-
1
def scope_key_by_partial(key)
-
if key.to_s.first == "."
-
if @virtual_path
-
@virtual_path.gsub(%r{/_?}, ".") + key.to_s
-
else
-
raise "Cannot use t(#{key.inspect}) shortcut because path is not available"
-
end
-
else
-
key
-
end
-
end
-
-
1
def html_safe_translation_key?(key)
-
key.to_s =~ /(\b|_|\.)html$/
-
end
-
end
-
end
-
end
-
1
require 'action_view/helpers/javascript_helper'
-
1
require 'active_support/core_ext/array/access'
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/core_ext/string/output_safety'
-
1
require 'action_dispatch'
-
-
1
module ActionView
-
# = Action View URL Helpers
-
1
module Helpers #:nodoc:
-
# Provides a set of methods for making links and getting URLs that
-
# depend on the routing subsystem (see ActionDispatch::Routing).
-
# This allows you to use the same format for links in views
-
# and controllers.
-
1
module UrlHelper
-
# This helper may be included in any class that includes the
-
# URL helpers of a routes (routes.url_helpers). Some methods
-
# provided here will only work in the context of a request
-
# (link_to_unless_current, for instance), which must be provided
-
# as a method called #request on the context.
-
-
1
extend ActiveSupport::Concern
-
-
1
include ActionDispatch::Routing::UrlFor
-
1
include TagHelper
-
-
1
def _routes_context
-
controller
-
end
-
-
# Need to map default url options to controller one.
-
# def default_url_options(*args) #:nodoc:
-
# controller.send(:default_url_options, *args)
-
# end
-
#
-
1
def url_options
-
return super unless controller.respond_to?(:url_options)
-
controller.url_options
-
end
-
-
# Returns the URL for the set of +options+ provided. This takes the
-
# same options as +url_for+ in Action Controller (see the
-
# documentation for <tt>ActionController::Base#url_for</tt>). Note that by default
-
# <tt>:only_path</tt> is <tt>true</tt> so you'll get the relative "/controller/action"
-
# instead of the fully qualified URL like "http://example.com/controller/action".
-
#
-
# ==== Options
-
# * <tt>:anchor</tt> - Specifies the anchor name to be appended to the path.
-
# * <tt>:only_path</tt> - If true, returns the relative URL (omitting the protocol, host name, and port) (<tt>true</tt> by default unless <tt>:host</tt> is specified).
-
# * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2005/". Note that this
-
# is currently not recommended since it breaks caching.
-
# * <tt>:host</tt> - Overrides the default (current) host if provided.
-
# * <tt>:protocol</tt> - Overrides the default (current) protocol if provided.
-
# * <tt>:user</tt> - Inline HTTP authentication (only plucked out if <tt>:password</tt> is also present).
-
# * <tt>:password</tt> - Inline HTTP authentication (only plucked out if <tt>:user</tt> is also present).
-
#
-
# ==== Relying on named routes
-
#
-
# Passing a record (like an Active Record or Active Resource) instead of a Hash as the options parameter will
-
# trigger the named route for that record. The lookup will happen on the name of the class. So passing a
-
# Workshop object will attempt to use the +workshop_path+ route. If you have a nested route, such as
-
# +admin_workshop_path+ you'll have to call that explicitly (it's impossible for +url_for+ to guess that route).
-
#
-
# ==== Examples
-
# <%= url_for(:action => 'index') %>
-
# # => /blog/
-
#
-
# <%= url_for(:action => 'find', :controller => 'books') %>
-
# # => /books/find
-
#
-
# <%= url_for(:action => 'login', :controller => 'members', :only_path => false, :protocol => 'https') %>
-
# # => https://www.example.com/members/login/
-
#
-
# <%= url_for(:action => 'play', :anchor => 'player') %>
-
# # => /messages/play/#player
-
#
-
# <%= url_for(:action => 'jump', :anchor => 'tax&ship') %>
-
# # => /testing/jump/#tax&ship
-
#
-
# <%= url_for(Workshop.new) %>
-
# # relies on Workshop answering a persisted? call (and in this case returning false)
-
# # => /workshops
-
#
-
# <%= url_for(@workshop) %>
-
# # calls @workshop.to_param which by default returns the id
-
# # => /workshops/5
-
#
-
# # to_param can be re-defined in a model to provide different URL names:
-
# # => /workshops/1-workshop-name
-
#
-
# <%= url_for("http://www.example.com") %>
-
# # => http://www.example.com
-
#
-
# <%= url_for(:back) %>
-
# # if request.env["HTTP_REFERER"] is set to "http://www.example.com"
-
# # => http://www.example.com
-
#
-
# <%= url_for(:back) %>
-
# # if request.env["HTTP_REFERER"] is not set or is blank
-
# # => javascript:history.back()
-
1
def url_for(options = {})
-
options ||= {}
-
case options
-
when String
-
options
-
when Hash
-
options = options.symbolize_keys.reverse_merge!(:only_path => options[:host].nil?)
-
super
-
when :back
-
controller.request.env["HTTP_REFERER"] || 'javascript:history.back()'
-
else
-
polymorphic_path(options)
-
end
-
end
-
-
# Creates a link tag of the given +name+ using a URL created by the set of +options+.
-
# See the valid options in the documentation for +url_for+. It's also possible to
-
# pass a String instead of an options hash, which generates a link tag that uses the
-
# value of the String as the href for the link. Using a <tt>:back</tt> Symbol instead
-
# of an options hash will generate a link to the referrer (a JavaScript back link
-
# will be used in place of a referrer if none exists). If +nil+ is passed as the name
-
# the value of the link itself will become the name.
-
#
-
# ==== Signatures
-
#
-
# link_to(body, url, html_options = {})
-
# # url is a String; you can use URL helpers like
-
# # posts_path
-
#
-
# link_to(body, url_options = {}, html_options = {})
-
# # url_options, except :confirm or :method,
-
# # is passed to url_for
-
#
-
# link_to(options = {}, html_options = {}) do
-
# # name
-
# end
-
#
-
# link_to(url, html_options = {}) do
-
# # name
-
# end
-
#
-
# ==== Options
-
# * <tt>:confirm => 'question?'</tt> - This will allow the unobtrusive JavaScript
-
# driver to prompt with the question specified. If the user accepts, the link is
-
# processed normally, otherwise no action is taken.
-
# * <tt>:method => symbol of HTTP verb</tt> - This modifier will dynamically
-
# create an HTML form and immediately submit the form for processing using
-
# the HTTP verb specified. Useful for having links perform a POST operation
-
# in dangerous actions like deleting a record (which search bots can follow
-
# while spidering your site). Supported verbs are <tt>:post</tt>, <tt>:delete</tt> and <tt>:put</tt>.
-
# Note that if the user has JavaScript disabled, the request will fall back
-
# to using GET. If <tt>:href => '#'</tt> is used and the user has JavaScript
-
# disabled clicking the link will have no effect. If you are relying on the
-
# POST behavior, you should check for it in your controller's action by using
-
# the request object's methods for <tt>post?</tt>, <tt>delete?</tt> or <tt>put?</tt>.
-
# * <tt>:remote => true</tt> - This will allow the unobtrusive JavaScript
-
# driver to make an Ajax request to the URL in question instead of following
-
# the link. The drivers each provide mechanisms for listening for the
-
# completion of the Ajax request and performing JavaScript operations once
-
# they're complete
-
#
-
# ==== Examples
-
# Because it relies on +url_for+, +link_to+ supports both older-style controller/action/id arguments
-
# and newer RESTful routes. Current Rails style favors RESTful routes whenever possible, so base
-
# your application on resources and use
-
#
-
# link_to "Profile", profile_path(@profile)
-
# # => <a href="/profiles/1">Profile</a>
-
#
-
# or the even pithier
-
#
-
# link_to "Profile", @profile
-
# # => <a href="/profiles/1">Profile</a>
-
#
-
# in place of the older more verbose, non-resource-oriented
-
#
-
# link_to "Profile", :controller => "profiles", :action => "show", :id => @profile
-
# # => <a href="/profiles/show/1">Profile</a>
-
#
-
# Similarly,
-
#
-
# link_to "Profiles", profiles_path
-
# # => <a href="/profiles">Profiles</a>
-
#
-
# is better than
-
#
-
# link_to "Profiles", :controller => "profiles"
-
# # => <a href="/profiles">Profiles</a>
-
#
-
# You can use a block as well if your link target is hard to fit into the name parameter. ERB example:
-
#
-
# <%= link_to(@profile) do %>
-
# <strong><%= @profile.name %></strong> -- <span>Check it out!</span>
-
# <% end %>
-
# # => <a href="/profiles/1">
-
# <strong>David</strong> -- <span>Check it out!</span>
-
# </a>
-
#
-
# Classes and ids for CSS are easy to produce:
-
#
-
# link_to "Articles", articles_path, :id => "news", :class => "article"
-
# # => <a href="/articles" class="article" id="news">Articles</a>
-
#
-
# Be careful when using the older argument style, as an extra literal hash is needed:
-
#
-
# link_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"
-
# # => <a href="/articles" class="article" id="news">Articles</a>
-
#
-
# Leaving the hash off gives the wrong link:
-
#
-
# link_to "WRONG!", :controller => "articles", :id => "news", :class => "article"
-
# # => <a href="/articles/index/news?class=article">WRONG!</a>
-
#
-
# +link_to+ can also produce links with anchors or query strings:
-
#
-
# link_to "Comment wall", profile_path(@profile, :anchor => "wall")
-
# # => <a href="/profiles/1#wall">Comment wall</a>
-
#
-
# link_to "Ruby on Rails search", :controller => "searches", :query => "ruby on rails"
-
# # => <a href="/searches?query=ruby+on+rails">Ruby on Rails search</a>
-
#
-
# link_to "Nonsense search", searches_path(:foo => "bar", :baz => "quux")
-
# # => <a href="/searches?foo=bar&baz=quux">Nonsense search</a>
-
#
-
# The two options specific to +link_to+ (<tt>:confirm</tt> and <tt>:method</tt>) are used as follows:
-
#
-
# link_to "Visit Other Site", "http://www.rubyonrails.org/", :confirm => "Are you sure?"
-
# # => <a href="http://www.rubyonrails.org/" data-confirm="Are you sure?"">Visit Other Site</a>
-
#
-
# link_to("Destroy", "http://www.example.com", :method => :delete, :confirm => "Are you sure?")
-
# # => <a href='http://www.example.com' rel="nofollow" data-method="delete" data-confirm="Are you sure?">Destroy</a>
-
1
def link_to(*args, &block)
-
if block_given?
-
options = args.first || {}
-
html_options = args.second
-
link_to(capture(&block), options, html_options)
-
else
-
name = args[0]
-
options = args[1] || {}
-
html_options = args[2]
-
-
html_options = convert_options_to_data_attributes(options, html_options)
-
url = url_for(options)
-
-
href = html_options['href']
-
tag_options = tag_options(html_options)
-
-
href_attr = "href=\"#{ERB::Util.html_escape(url)}\"" unless href
-
"<a #{href_attr}#{tag_options}>#{ERB::Util.html_escape(name || url)}</a>".html_safe
-
end
-
end
-
-
# Generates a form containing a single button that submits to the URL created
-
# by the set of +options+. This is the safest method to ensure links that
-
# cause changes to your data are not triggered by search bots or accelerators.
-
# If the HTML button does not work with your layout, you can also consider
-
# using the +link_to+ method with the <tt>:method</tt> modifier as described in
-
# the +link_to+ documentation.
-
#
-
# By default, the generated form element has a class name of <tt>button_to</tt>
-
# to allow styling of the form itself and its children. This can be changed
-
# using the <tt>:form_class</tt> modifier within +html_options+. You can control
-
# the form submission and input element behavior using +html_options+.
-
# This method accepts the <tt>:method</tt> and <tt>:confirm</tt> modifiers
-
# described in the +link_to+ documentation. If no <tt>:method</tt> modifier
-
# is given, it will default to performing a POST operation. You can also
-
# disable the button by passing <tt>:disabled => true</tt> in +html_options+.
-
# If you are using RESTful routes, you can pass the <tt>:method</tt>
-
# to change the HTTP verb used to submit the form.
-
#
-
# ==== Options
-
# The +options+ hash accepts the same options as +url_for+.
-
#
-
# There are a few special +html_options+:
-
# * <tt>:method</tt> - Symbol of HTTP verb. Supported verbs are <tt>:post</tt>, <tt>:get</tt>,
-
# <tt>:delete</tt> and <tt>:put</tt>. By default it will be <tt>:post</tt>.
-
# * <tt>:disabled</tt> - If set to true, it will generate a disabled button.
-
# * <tt>:confirm</tt> - This will use the unobtrusive JavaScript driver to
-
# prompt with the question specified. If the user accepts, the link is
-
# processed normally, otherwise no action is taken.
-
# * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the
-
# submit behavior. By default this behavior is an ajax submit.
-
# * <tt>:form_class</tt> - This controls the class of the form within which the submit button will
-
# be placed
-
#
-
# ==== Examples
-
# <%= button_to "New", :action => "new" %>
-
# # => "<form method="post" action="/controller/new" class="button_to">
-
# # <div><input value="New" type="submit" /></div>
-
# # </form>"
-
#
-
#
-
# <%= button_to "New", :action => "new", :form_class => "new-thing" %>
-
# # => "<form method="post" action="/controller/new" class="new-thing">
-
# # <div><input value="New" type="submit" /></div>
-
# # </form>"
-
#
-
#
-
# <%= button_to "Delete Image", { :action => "delete", :id => @image.id },
-
# :confirm => "Are you sure?", :method => :delete %>
-
# # => "<form method="post" action="/images/delete/1" class="button_to">
-
# # <div>
-
# # <input type="hidden" name="_method" value="delete" />
-
# # <input data-confirm='Are you sure?' value="Delete" type="submit" />
-
# # </div>
-
# # </form>"
-
#
-
#
-
# <%= button_to('Destroy', 'http://www.example.com', :confirm => 'Are you sure?',
-
# :method => "delete", :remote => true, :disable_with => 'loading...') %>
-
# # => "<form class='button_to' method='post' action='http://www.example.com' data-remote='true'>
-
# # <div>
-
# # <input name='_method' value='delete' type='hidden' />
-
# # <input value='Destroy' type='submit' disable_with='loading...' data-confirm='Are you sure?' />
-
# # </div>
-
# # </form>"
-
# #
-
1
def button_to(name, options = {}, html_options = {})
-
html_options = html_options.stringify_keys
-
convert_boolean_attributes!(html_options, %w( disabled ))
-
-
method_tag = ''
-
if (method = html_options.delete('method')) && %w{put delete}.include?(method.to_s)
-
method_tag = tag('input', :type => 'hidden', :name => '_method', :value => method.to_s)
-
end
-
-
form_method = method.to_s == 'get' ? 'get' : 'post'
-
form_class = html_options.delete('form_class') || 'button_to'
-
-
remote = html_options.delete('remote')
-
-
request_token_tag = ''
-
if form_method == 'post' && protect_against_forgery?
-
request_token_tag = tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token)
-
end
-
-
url = options.is_a?(String) ? options : self.url_for(options)
-
name ||= url
-
-
html_options = convert_options_to_data_attributes(options, html_options)
-
-
html_options.merge!("type" => "submit", "value" => name)
-
-
("<form method=\"#{form_method}\" action=\"#{ERB::Util.html_escape(url)}\" #{"data-remote=\"true\"" if remote} class=\"#{ERB::Util.html_escape(form_class)}\"><div>" +
-
method_tag + tag("input", html_options) + request_token_tag + "</div></form>").html_safe
-
end
-
-
-
# Creates a link tag of the given +name+ using a URL created by the set of
-
# +options+ unless the current request URI is the same as the links, in
-
# which case only the name is returned (or the given block is yielded, if
-
# one exists). You can give +link_to_unless_current+ a block which will
-
# specialize the default behavior (e.g., show a "Start Here" link rather
-
# than the link's text).
-
#
-
# ==== Examples
-
# Let's say you have a navigation menu...
-
#
-
# <ul id="navbar">
-
# <li><%= link_to_unless_current("Home", { :action => "index" }) %></li>
-
# <li><%= link_to_unless_current("About Us", { :action => "about" }) %></li>
-
# </ul>
-
#
-
# If in the "about" action, it will render...
-
#
-
# <ul id="navbar">
-
# <li><a href="/controller/index">Home</a></li>
-
# <li>About Us</li>
-
# </ul>
-
#
-
# ...but if in the "index" action, it will render:
-
#
-
# <ul id="navbar">
-
# <li>Home</li>
-
# <li><a href="/controller/about">About Us</a></li>
-
# </ul>
-
#
-
# The implicit block given to +link_to_unless_current+ is evaluated if the current
-
# action is the action given. So, if we had a comments page and wanted to render a
-
# "Go Back" link instead of a link to the comments page, we could do something like this...
-
#
-
# <%=
-
# link_to_unless_current("Comment", { :controller => "comments", :action => "new" }) do
-
# link_to("Go back", { :controller => "posts", :action => "index" })
-
# end
-
# %>
-
1
def link_to_unless_current(name, options = {}, html_options = {}, &block)
-
link_to_unless current_page?(options), name, options, html_options, &block
-
end
-
-
# Creates a link tag of the given +name+ using a URL created by the set of
-
# +options+ unless +condition+ is true, in which case only the name is
-
# returned. To specialize the default behavior (i.e., show a login link rather
-
# than just the plaintext link text), you can pass a block that
-
# accepts the name or the full argument list for +link_to_unless+.
-
#
-
# ==== Examples
-
# <%= link_to_unless(@current_user.nil?, "Reply", { :action => "reply" }) %>
-
# # If the user is logged in...
-
# # => <a href="/controller/reply/">Reply</a>
-
#
-
# <%=
-
# link_to_unless(@current_user.nil?, "Reply", { :action => "reply" }) do |name|
-
# link_to(name, { :controller => "accounts", :action => "signup" })
-
# end
-
# %>
-
# # If the user is logged in...
-
# # => <a href="/controller/reply/">Reply</a>
-
# # If not...
-
# # => <a href="/accounts/signup">Reply</a>
-
1
def link_to_unless(condition, name, options = {}, html_options = {}, &block)
-
if condition
-
if block_given?
-
block.arity <= 1 ? capture(name, &block) : capture(name, options, html_options, &block)
-
else
-
name
-
end
-
else
-
link_to(name, options, html_options)
-
end
-
end
-
-
# Creates a link tag of the given +name+ using a URL created by the set of
-
# +options+ if +condition+ is true, otherwise only the name is
-
# returned. To specialize the default behavior, you can pass a block that
-
# accepts the name or the full argument list for +link_to_unless+ (see the examples
-
# in +link_to_unless+).
-
#
-
# ==== Examples
-
# <%= link_to_if(@current_user.nil?, "Login", { :controller => "sessions", :action => "new" }) %>
-
# # If the user isn't logged in...
-
# # => <a href="/sessions/new/">Login</a>
-
#
-
# <%=
-
# link_to_if(@current_user.nil?, "Login", { :controller => "sessions", :action => "new" }) do
-
# link_to(@current_user.login, { :controller => "accounts", :action => "show", :id => @current_user })
-
# end
-
# %>
-
# # If the user isn't logged in...
-
# # => <a href="/sessions/new/">Login</a>
-
# # If they are logged in...
-
# # => <a href="/accounts/show/3">my_username</a>
-
1
def link_to_if(condition, name, options = {}, html_options = {}, &block)
-
link_to_unless !condition, name, options, html_options, &block
-
end
-
-
# Creates a mailto link tag to the specified +email_address+, which is
-
# also used as the name of the link unless +name+ is specified. Additional
-
# HTML attributes for the link can be passed in +html_options+.
-
#
-
# +mail_to+ has several methods for hindering email harvesters and customizing
-
# the email itself by passing special keys to +html_options+.
-
#
-
# ==== Options
-
# * <tt>:encode</tt> - This key will accept the strings "javascript" or "hex".
-
# Passing "javascript" will dynamically create and encode the mailto link then
-
# eval it into the DOM of the page. This method will not show the link on
-
# the page if the user has JavaScript disabled. Passing "hex" will hex
-
# encode the +email_address+ before outputting the mailto link.
-
# * <tt>:replace_at</tt> - When the link +name+ isn't provided, the
-
# +email_address+ is used for the link label. You can use this option to
-
# obfuscate the +email_address+ by substituting the @ sign with the string
-
# given as the value.
-
# * <tt>:replace_dot</tt> - When the link +name+ isn't provided, the
-
# +email_address+ is used for the link label. You can use this option to
-
# obfuscate the +email_address+ by substituting the . in the email with the
-
# string given as the value.
-
# * <tt>:subject</tt> - Preset the subject line of the email.
-
# * <tt>:body</tt> - Preset the body of the email.
-
# * <tt>:cc</tt> - Carbon Copy addition recipients on the email.
-
# * <tt>:bcc</tt> - Blind Carbon Copy additional recipients on the email.
-
#
-
# ==== Examples
-
# mail_to "me@domain.com"
-
# # => <a href="mailto:me@domain.com">me@domain.com</a>
-
#
-
# mail_to "me@domain.com", "My email", :encode => "javascript"
-
# # => <script type="text/javascript">eval(decodeURIComponent('%64%6f%63...%27%29%3b'))</script>
-
#
-
# mail_to "me@domain.com", "My email", :encode => "hex"
-
# # => <a href="mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">My email</a>
-
#
-
# mail_to "me@domain.com", nil, :replace_at => "_at_", :replace_dot => "_dot_", :class => "email"
-
# # => <a href="mailto:me@domain.com" class="email">me_at_domain_dot_com</a>
-
#
-
# mail_to "me@domain.com", "My email", :cc => "ccaddress@domain.com",
-
# :subject => "This is an example email"
-
# # => <a href="mailto:me@domain.com?cc=ccaddress@domain.com&subject=This%20is%20an%20example%20email">My email</a>
-
1
def mail_to(email_address, name = nil, html_options = {})
-
email_address = ERB::Util.html_escape(email_address)
-
-
html_options = html_options.stringify_keys
-
encode = html_options.delete("encode").to_s
-
-
extras = %w{ cc bcc body subject }.map { |item|
-
option = html_options.delete(item) || next
-
"#{item}=#{Rack::Utils.escape(option).gsub("+", "%20")}"
-
}.compact
-
extras = extras.empty? ? '' : '?' + ERB::Util.html_escape(extras.join('&'))
-
-
email_address_obfuscated = email_address.to_str
-
email_address_obfuscated.gsub!(/@/, html_options.delete("replace_at")) if html_options.key?("replace_at")
-
email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.key?("replace_dot")
-
case encode
-
when "javascript"
-
string = ''
-
html = content_tag("a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe))
-
html = escape_javascript(html.to_str)
-
"document.write('#{html}');".each_byte do |c|
-
string << sprintf("%%%x", c)
-
end
-
"<script type=\"#{Mime::JS}\">eval(decodeURIComponent('#{string}'))</script>".html_safe
-
when "hex"
-
email_address_encoded = email_address_obfuscated.unpack('C*').map {|c|
-
sprintf("&#%d;", c)
-
}.join
-
-
string = 'mailto:'.unpack('C*').map { |c|
-
sprintf("&#%d;", c)
-
}.join + email_address.unpack('C*').map { |c|
-
char = c.chr
-
char =~ /\w/ ? sprintf("%%%x", c) : char
-
}.join
-
-
content_tag "a", name || email_address_encoded.html_safe, html_options.merge("href" => "#{string}#{extras}".html_safe)
-
else
-
content_tag "a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe)
-
end
-
end
-
-
# True if the current request URI was generated by the given +options+.
-
#
-
# ==== Examples
-
# Let's say we're in the <tt>/shop/checkout?order=desc</tt> action.
-
#
-
# current_page?(:action => 'process')
-
# # => false
-
#
-
# current_page?(:controller => 'shop', :action => 'checkout')
-
# # => true
-
#
-
# current_page?(:controller => 'shop', :action => 'checkout', :order => 'asc')
-
# # => false
-
#
-
# current_page?(:action => 'checkout')
-
# # => true
-
#
-
# current_page?(:controller => 'library', :action => 'checkout')
-
# # => false
-
#
-
# Let's say we're in the <tt>/shop/checkout?order=desc&page=1</tt> action.
-
#
-
# current_page?(:action => 'process')
-
# # => false
-
#
-
# current_page?(:controller => 'shop', :action => 'checkout')
-
# # => true
-
#
-
# current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc', :page => '1')
-
# # => true
-
#
-
# current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc', :page => '2')
-
# # => false
-
#
-
# current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc')
-
# # => false
-
#
-
# current_page?(:action => 'checkout')
-
# # => true
-
#
-
# current_page?(:controller => 'library', :action => 'checkout')
-
# # => false
-
1
def current_page?(options)
-
unless request
-
raise "You cannot use helpers that need to determine the current " \
-
"page unless your view context provides a Request object " \
-
"in a #request method"
-
end
-
-
url_string = url_for(options)
-
-
# We ignore any extra parameters in the request_uri if the
-
# submitted url doesn't have any either. This lets the function
-
# work with things like ?order=asc
-
if url_string.index("?")
-
request_uri = request.fullpath
-
else
-
request_uri = request.path
-
end
-
-
if url_string =~ /^\w+:\/\//
-
url_string == "#{request.protocol}#{request.host_with_port}#{request_uri}"
-
else
-
url_string == request_uri
-
end
-
end
-
-
1
private
-
1
def convert_options_to_data_attributes(options, html_options)
-
if html_options.nil?
-
link_to_remote_options?(options) ? {'data-remote' => 'true'} : {}
-
else
-
html_options = html_options.stringify_keys
-
html_options['data-remote'] = 'true' if link_to_remote_options?(options) || link_to_remote_options?(html_options)
-
-
disable_with = html_options.delete("disable_with")
-
confirm = html_options.delete('confirm')
-
method = html_options.delete('method')
-
-
html_options["data-disable-with"] = disable_with if disable_with
-
html_options["data-confirm"] = confirm if confirm
-
add_method_to_attributes!(html_options, method) if method
-
-
html_options
-
end
-
end
-
-
1
def link_to_remote_options?(options)
-
options.is_a?(Hash) && options.key?('remote') && options.delete('remote')
-
end
-
-
1
def add_method_to_attributes!(html_options, method)
-
html_options["rel"] = "nofollow" if method.to_s.downcase != "get"
-
html_options["data-method"] = method
-
end
-
-
1
def options_for_javascript(options)
-
if options.empty?
-
'{}'
-
else
-
"{#{options.keys.map { |k| "#{k}:#{options[k]}" }.sort.join(', ')}}"
-
end
-
end
-
-
1
def array_or_string_for_javascript(option)
-
if option.kind_of?(Array)
-
"['#{option.join('\',\'')}']"
-
elsif !option.nil?
-
"'#{option}'"
-
end
-
end
-
-
# Processes the +html_options+ hash, converting the boolean
-
# attributes from true/false form into the form required by
-
# HTML/XHTML. (An attribute is considered to be boolean if
-
# its name is listed in the given +bool_attrs+ array.)
-
#
-
# More specifically, for each boolean attribute in +html_options+
-
# given as:
-
#
-
# "attr" => bool_value
-
#
-
# if the associated +bool_value+ evaluates to true, it is
-
# replaced with the attribute's name; otherwise the attribute is
-
# removed from the +html_options+ hash. (See the XHTML 1.0 spec,
-
# section 4.5 "Attribute Minimization" for more:
-
# http://www.w3.org/TR/xhtml1/#h-4.5)
-
#
-
# Returns the updated +html_options+ hash, which is also modified
-
# in place.
-
#
-
# Example:
-
#
-
# convert_boolean_attributes!( html_options,
-
# %w( checked disabled readonly ) )
-
1
def convert_boolean_attributes!(html_options, bool_attrs)
-
bool_attrs.each { |x| html_options[x] = x if html_options.delete(x) }
-
html_options
-
end
-
end
-
end
-
end
-
1
module ActionView
-
# = Action View Log Subscriber
-
#
-
# Provides functionality so that Rails can output logs from Action View.
-
1
class LogSubscriber < ActiveSupport::LogSubscriber
-
1
def render_template(event)
-
message = "Rendered #{from_rails_root(event.payload[:identifier])}"
-
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
-
message << (" (%.1fms)" % event.duration)
-
info(message)
-
end
-
1
alias :render_partial :render_template
-
1
alias :render_collection :render_template
-
-
# TODO: Ideally, ActionView should have its own logger so it does not depend on AC.logger
-
1
def logger
-
ActionController::Base.logger if defined?(ActionController::Base)
-
end
-
-
1
protected
-
-
1
def from_rails_root(string)
-
string.sub("#{Rails.root}/", "").sub(/^app\/views\//, "")
-
end
-
end
-
end
-
-
1
ActionView::LogSubscriber.attach_to :action_view
-
1
module ActionView #:nodoc:
-
# = Action View PathSet
-
1
class PathSet < Array #:nodoc:
-
1
%w(initialize << concat insert push unshift).each do |method|
-
6
class_eval <<-METHOD, __FILE__, __LINE__ + 1
-
def #{method}(*args)
-
super
-
typecast!
-
end
-
METHOD
-
end
-
-
1
def find(*args)
-
find_all(*args).first || raise(MissingTemplate.new(self, *args))
-
end
-
-
1
def find_all(path, prefixes = [], *args)
-
prefixes = [prefixes] if String === prefixes
-
prefixes.each do |prefix|
-
each do |resolver|
-
templates = resolver.find_all(path, prefix, *args)
-
return templates unless templates.empty?
-
end
-
end
-
[]
-
end
-
-
1
def exists?(*args)
-
find_all(*args).any?
-
end
-
-
1
protected
-
-
1
def typecast!
-
2
each_with_index do |path, i|
-
1
path = path.to_s if path.is_a?(Pathname)
-
1
next unless path.is_a?(String)
-
1
self[i] = OptimizedFileSystemResolver.new(path)
-
end
-
end
-
end
-
end
-
1
require "action_view"
-
1
require "rails"
-
-
1
module ActionView
-
# = Action View Railtie
-
1
class Railtie < Rails::Railtie
-
1
config.action_view = ActiveSupport::OrderedOptions.new
-
1
config.action_view.stylesheet_expansions = {}
-
1
config.action_view.javascript_expansions = { :defaults => %w(jquery jquery_ujs) }
-
-
1
initializer "action_view.cache_asset_ids" do |app|
-
1
unless app.config.cache_classes
-
ActiveSupport.on_load(:action_view) do
-
ActionView::Helpers::AssetTagHelper::AssetPaths.cache_asset_ids = false
-
end
-
end
-
end
-
-
1
initializer "action_view.javascript_expansions" do |app|
-
1
ActiveSupport.on_load(:action_view) do
-
1
ActionView::Helpers::AssetTagHelper.register_javascript_expansion(
-
app.config.action_view.delete(:javascript_expansions)
-
)
-
-
1
ActionView::Helpers::AssetTagHelper.register_stylesheet_expansion(
-
app.config.action_view.delete(:stylesheet_expansions)
-
)
-
end
-
end
-
-
1
initializer "action_view.set_configs" do |app|
-
1
ActiveSupport.on_load(:action_view) do
-
1
app.config.action_view.each do |k,v|
-
1
send "#{k}=", v
-
end
-
end
-
end
-
-
1
initializer "action_view.caching" do |app|
-
1
ActiveSupport.on_load(:action_view) do
-
1
if app.config.action_view.cache_template_loading.nil?
-
1
ActionView::Resolver.caching = app.config.cache_classes
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/object/try'
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
-
1
module ActionView
-
# = Action View Template
-
1
class Template
-
1
extend ActiveSupport::Autoload
-
-
# === Encodings in ActionView::Template
-
#
-
# ActionView::Template is one of a few sources of potential
-
# encoding issues in Rails. This is because the source for
-
# templates are usually read from disk, and Ruby (like most
-
# encoding-aware programming languages) assumes that the
-
# String retrieved through File IO is encoded in the
-
# <tt>default_external</tt> encoding. In Rails, the default
-
# <tt>default_external</tt> encoding is UTF-8.
-
#
-
# As a result, if a user saves their template as ISO-8859-1
-
# (for instance, using a non-Unicode-aware text editor),
-
# and uses characters outside of the ASCII range, their
-
# users will see diamonds with question marks in them in
-
# the browser.
-
#
-
# For the rest of this documentation, when we say "UTF-8",
-
# we mean "UTF-8 or whatever the default_internal encoding
-
# is set to". By default, it will be UTF-8.
-
#
-
# To mitigate this problem, we use a few strategies:
-
# 1. If the source is not valid UTF-8, we raise an exception
-
# when the template is compiled to alert the user
-
# to the problem.
-
# 2. The user can specify the encoding using Ruby-style
-
# encoding comments in any template engine. If such
-
# a comment is supplied, Rails will apply that encoding
-
# to the resulting compiled source returned by the
-
# template handler.
-
# 3. In all cases, we transcode the resulting String to
-
# the UTF-8.
-
#
-
# This means that other parts of Rails can always assume
-
# that templates are encoded in UTF-8, even if the original
-
# source of the template was not UTF-8.
-
#
-
# From a user's perspective, the easiest thing to do is
-
# to save your templates as UTF-8. If you do this, you
-
# do not need to do anything else for things to "just work".
-
#
-
# === Instructions for template handlers
-
#
-
# The easiest thing for you to do is to simply ignore
-
# encodings. Rails will hand you the template source
-
# as the default_internal (generally UTF-8), raising
-
# an exception for the user before sending the template
-
# to you if it could not determine the original encoding.
-
#
-
# For the greatest simplicity, you can support only
-
# UTF-8 as the <tt>default_internal</tt>. This means
-
# that from the perspective of your handler, the
-
# entire pipeline is just UTF-8.
-
#
-
# === Advanced: Handlers with alternate metadata sources
-
#
-
# If you want to provide an alternate mechanism for
-
# specifying encodings (like ERB does via <%# encoding: ... %>),
-
# you may indicate that you will handle encodings yourself
-
# by implementing <tt>self.handles_encoding?</tt>
-
# on your handler.
-
#
-
# If you do, Rails will not try to encode the String
-
# into the default_internal, passing you the unaltered
-
# bytes tagged with the assumed encoding (from
-
# default_external).
-
#
-
# In this case, make sure you return a String from
-
# your handler encoded in the default_internal. Since
-
# you are handling out-of-band metadata, you are
-
# also responsible for alerting the user to any
-
# problems with converting the user's data to
-
# the <tt>default_internal</tt>.
-
#
-
# To do so, simply raise the raise +WrongEncodingError+
-
# as follows:
-
#
-
# raise WrongEncodingError.new(
-
# problematic_string,
-
# expected_encoding
-
# )
-
-
1
eager_autoload do
-
1
autoload :Error
-
1
autoload :Handler
-
1
autoload :Handlers
-
1
autoload :Text
-
end
-
-
1
extend Template::Handlers
-
-
1
attr_accessor :locals, :formats, :virtual_path
-
-
1
attr_reader :source, :identifier, :handler, :original_encoding, :updated_at
-
-
# This finalizer is needed (and exactly with a proc inside another proc)
-
# otherwise templates leak in development.
-
1
Finalizer = proc do |method_name, mod|
-
proc do
-
mod.module_eval do
-
remove_possible_method method_name
-
end
-
end
-
end
-
-
1
def initialize(source, identifier, handler, details)
-
format = details[:format] || (handler.default_format if handler.respond_to?(:default_format))
-
-
@source = source
-
@identifier = identifier
-
@handler = handler
-
@compiled = false
-
@original_encoding = nil
-
@locals = details[:locals] || []
-
@virtual_path = details[:virtual_path]
-
@updated_at = details[:updated_at] || Time.now
-
@formats = Array.wrap(format).map { |f| f.is_a?(Mime::Type) ? f.ref : f }
-
end
-
-
# Returns if the underlying handler supports streaming. If so,
-
# a streaming buffer *may* be passed when it start rendering.
-
1
def supports_streaming?
-
handler.respond_to?(:supports_streaming?) && handler.supports_streaming?
-
end
-
-
# Render a template. If the template was not compiled yet, it is done
-
# exactly before rendering.
-
#
-
# This method is instrumented as "!render_template.action_view". Notice that
-
# we use a bang in this instrumentation because you don't want to
-
# consume this in production. This is only slow if it's being listened to.
-
1
def render(view, locals, buffer=nil, &block)
-
ActiveSupport::Notifications.instrument("!render_template.action_view", :virtual_path => @virtual_path) do
-
compile!(view)
-
view.send(method_name, locals, buffer, &block)
-
end
-
rescue Exception => e
-
handle_render_error(view, e)
-
end
-
-
1
def mime_type
-
@mime_type ||= Mime::Type.lookup_by_extension(@formats.first.to_s) if @formats.first
-
end
-
-
# Receives a view object and return a template similar to self by using @virtual_path.
-
#
-
# This method is useful if you have a template object but it does not contain its source
-
# anymore since it was already compiled. In such cases, all you need to do is to call
-
# refresh passing in the view object.
-
#
-
# Notice this method raises an error if the template to be refreshed does not have a
-
# virtual path set (true just for inline templates).
-
1
def refresh(view)
-
raise "A template needs to have a virtual path in order to be refreshed" unless @virtual_path
-
lookup = view.lookup_context
-
pieces = @virtual_path.split("/")
-
name = pieces.pop
-
partial = !!name.sub!(/^_/, "")
-
lookup.disable_cache do
-
lookup.find_template(name, [ pieces.join('/') ], partial, @locals)
-
end
-
end
-
-
1
def inspect
-
@inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", '') : identifier
-
end
-
-
1
protected
-
-
# Compile a template. This method ensures a template is compiled
-
# just once and removes the source after it is compiled.
-
1
def compile!(view) #:nodoc:
-
return if @compiled
-
-
if view.is_a?(ActionView::CompiledTemplates)
-
mod = ActionView::CompiledTemplates
-
else
-
mod = view.singleton_class
-
end
-
-
compile(view, mod)
-
-
# Just discard the source if we have a virtual path. This
-
# means we can get the template back.
-
@source = nil if @virtual_path
-
@compiled = true
-
end
-
-
# Among other things, this method is responsible for properly setting
-
# the encoding of the source. Until this point, we assume that the
-
# source is BINARY data. If no additional information is supplied,
-
# we assume the encoding is the same as <tt>Encoding.default_external</tt>.
-
#
-
# The user can also specify the encoding via a comment on the first
-
# line of the template (# encoding: NAME-OF-ENCODING). This will work
-
# with any template engine, as we process out the encoding comment
-
# before passing the source on to the template engine, leaving a
-
# blank line in its stead.
-
#
-
# If the template engine handles encodings, we send the encoded
-
# String to the engine without further processing. This allows
-
# the template engine to support additional mechanisms for
-
# specifying the encoding. For instance, ERB supports <%# encoding: %>
-
#
-
# Otherwise, after we figure out the correct encoding, we then
-
# encode the source into <tt>Encoding.default_internal</tt>.
-
# In general, this means that templates will be UTF-8 inside of Rails,
-
# regardless of the original source encoding.
-
1
def compile(view, mod) #:nodoc:
-
method_name = self.method_name
-
-
if source.encoding_aware?
-
# Look for # encoding: *. If we find one, we'll encode the
-
# String in that encoding, otherwise, we'll use the
-
# default external encoding.
-
if source.sub!(/\A#{ENCODING_FLAG}/, '')
-
encoding = magic_encoding = $1
-
else
-
encoding = Encoding.default_external
-
end
-
-
# Tag the source with the default external encoding
-
# or the encoding specified in the file
-
source.force_encoding(encoding)
-
-
# If the user didn't specify an encoding, and the handler
-
# handles encodings, we simply pass the String as is to
-
# the handler (with the default_external tag)
-
if !magic_encoding && @handler.respond_to?(:handles_encoding?) && @handler.handles_encoding?
-
source
-
# Otherwise, if the String is valid in the encoding,
-
# encode immediately to default_internal. This means
-
# that if a handler doesn't handle encodings, it will
-
# always get Strings in the default_internal
-
elsif source.valid_encoding?
-
source.encode!
-
# Otherwise, since the String is invalid in the encoding
-
# specified, raise an exception
-
else
-
raise WrongEncodingError.new(source, encoding)
-
end
-
end
-
-
code = @handler.call(self)
-
-
# Make sure that the resulting String to be evalled is in the
-
# encoding of the code
-
source = <<-end_src
-
def #{method_name}(local_assigns, output_buffer)
-
_old_virtual_path, @virtual_path = @virtual_path, #{@virtual_path.inspect};_old_output_buffer = @output_buffer;#{locals_code};#{code}
-
ensure
-
@virtual_path, @output_buffer = _old_virtual_path, _old_output_buffer
-
end
-
end_src
-
-
if source.encoding_aware?
-
# Make sure the source is in the encoding of the returned code
-
source.force_encoding(code.encoding)
-
-
# In case we get back a String from a handler that is not in
-
# BINARY or the default_internal, encode it to the default_internal
-
source.encode!
-
-
# Now, validate that the source we got back from the template
-
# handler is valid in the default_internal. This is for handlers
-
# that handle encoding but screw up
-
unless source.valid_encoding?
-
raise WrongEncodingError.new(@source, Encoding.default_internal)
-
end
-
end
-
-
begin
-
mod.module_eval(source, identifier, 0)
-
ObjectSpace.define_finalizer(self, Finalizer[method_name, mod])
-
rescue Exception => e # errors from template code
-
if logger = (view && view.logger)
-
logger.debug "ERROR: compiling #{method_name} RAISED #{e}"
-
logger.debug "Function body: #{source}"
-
logger.debug "Backtrace: #{e.backtrace.join("\n")}"
-
end
-
-
raise ActionView::Template::Error.new(self, {}, e)
-
end
-
end
-
-
1
def handle_render_error(view, e) #:nodoc:
-
if e.is_a?(Template::Error)
-
e.sub_template_of(self)
-
raise e
-
else
-
assigns = view.respond_to?(:assigns) ? view.assigns : {}
-
template = @virtual_path ? refresh(view) : self
-
raise Template::Error.new(template, assigns, e)
-
end
-
end
-
-
1
def locals_code #:nodoc:
-
@locals.map { |key| "#{key} = local_assigns[:#{key}];" }.join
-
end
-
-
1
def method_name #:nodoc:
-
@method_name ||= "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}".gsub('-', "_")
-
end
-
-
1
def identifier_method_name #:nodoc:
-
inspect.gsub(/[^a-z_]/, '_')
-
end
-
end
-
end
-
1
require 'action_dispatch/http/mime_type'
-
1
require 'active_support/core_ext/class/attribute'
-
-
# Legacy TemplateHandler stub
-
1
module ActionView
-
1
class Template
-
1
module Handlers #:nodoc:
-
1
module Compilable
-
1
def self.included(base)
-
ActiveSupport::Deprecation.warn "Including Compilable in your template handler is deprecated. " <<
-
"Since Rails 3, all the API your template handler needs to implement is to respond to #call."
-
base.extend(ClassMethods)
-
end
-
-
1
module ClassMethods
-
1
def call(template)
-
new.compile(template)
-
end
-
end
-
-
1
def compile(template)
-
raise "Need to implement #{self.class.name}#compile(template)"
-
end
-
end
-
end
-
-
1
class Template::Handler
-
1
class_attribute :default_format
-
1
self.default_format = Mime::HTML
-
-
1
def self.inherited(base)
-
ActiveSupport::Deprecation.warn "Inheriting from ActionView::Template::Handler is deprecated. " <<
-
"Since Rails 3, all the API your template handler needs to implement is to respond to #call."
-
super
-
end
-
-
1
def self.call(template)
-
raise "Need to implement #{self.class.name}#call(template)"
-
end
-
-
1
def render(template, local_assigns)
-
raise "Need to implement #{self.class.name}#render(template, local_assigns)"
-
end
-
end
-
end
-
-
1
TemplateHandlers = Template::Handlers
-
1
TemplateHandler = Template::Handler
-
end
-
1
module ActionView #:nodoc:
-
# = Action View Template Handlers
-
1
class Template
-
1
module Handlers #:nodoc:
-
1
autoload :ERB, 'action_view/template/handlers/erb'
-
1
autoload :Builder, 'action_view/template/handlers/builder'
-
-
1
def self.extended(base)
-
1
base.register_default_template_handler :erb, ERB.new
-
1
base.register_template_handler :builder, Builder.new
-
end
-
-
1
@@template_handlers = {}
-
1
@@default_template_handlers = nil
-
-
1
def self.extensions
-
@@template_extensions ||= @@template_handlers.keys
-
end
-
-
# Register a class that knows how to handle template files with the given
-
# extension. This can be used to implement new template types.
-
# The constructor for the class must take the ActiveView::Base instance
-
# as a parameter, and the class must implement a +render+ method that
-
# takes the contents of the template to render as well as the Hash of
-
# local assigns available to the template. The +render+ method ought to
-
# return the rendered template as a string.
-
1
def register_template_handler(extension, klass)
-
3
@@template_handlers[extension.to_sym] = klass
-
end
-
-
1
def template_handler_extensions
-
@@template_handlers.keys.map {|key| key.to_s }.sort
-
end
-
-
1
def registered_template_handler(extension)
-
extension && @@template_handlers[extension.to_sym]
-
end
-
-
1
def register_default_template_handler(extension, klass)
-
1
register_template_handler(extension, klass)
-
1
@@default_template_handlers = klass
-
end
-
-
1
def handler_class_for_extension(extension)
-
ActiveSupport::Deprecation.warn "handler_class_for_extension is deprecated. " <<
-
"Please use handler_for_extension instead", caller
-
handler_for_extension(extension)
-
end
-
-
1
def handler_for_extension(extension)
-
registered_template_handler(extension) || @@default_template_handlers
-
end
-
end
-
end
-
end
-
1
module ActionView
-
1
module Template::Handlers
-
1
class Builder
-
# Default format used by Builder.
-
1
class_attribute :default_format
-
1
self.default_format = Mime::XML
-
-
1
def call(template)
-
require 'builder'
-
"xml = ::Builder::XmlMarkup.new(:indent => 2);" +
-
"self.output_buffer = xml.target!;" +
-
template.source +
-
";xml.target!;"
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/class/attribute_accessors'
-
1
require 'action_view/template/handler'
-
1
require 'erubis'
-
-
1
module ActionView
-
1
class Template
-
1
module Handlers
-
1
class Erubis < ::Erubis::Eruby
-
1
def add_preamble(src)
-
src << "@output_buffer = output_buffer || ActionView::OutputBuffer.new;"
-
end
-
-
1
def add_text(src, text)
-
return if text.empty?
-
src << "@output_buffer.safe_concat('" << escape_text(text) << "');"
-
end
-
-
1
BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/
-
-
1
def add_expr_literal(src, code)
-
if code =~ BLOCK_EXPR
-
src << '@output_buffer.append= ' << code
-
else
-
src << '@output_buffer.append= (' << code << ');'
-
end
-
end
-
-
1
def add_expr_escaped(src, code)
-
if code =~ BLOCK_EXPR
-
src << "@output_buffer.safe_append= " << code
-
else
-
src << "@output_buffer.safe_concat((" << code << ").to_s);"
-
end
-
end
-
-
1
def add_postamble(src)
-
src << '@output_buffer.to_s'
-
end
-
end
-
-
1
class ERB
-
# Specify trim mode for the ERB compiler. Defaults to '-'.
-
# See ERB documentation for suitable values.
-
1
class_attribute :erb_trim_mode
-
1
self.erb_trim_mode = '-'
-
-
# Default format used by ERB.
-
1
class_attribute :default_format
-
1
self.default_format = Mime::HTML
-
-
# Default implementation used.
-
1
class_attribute :erb_implementation
-
1
self.erb_implementation = Erubis
-
-
1
ENCODING_TAG = Regexp.new("\\A(<%#{ENCODING_FLAG}-?%>)[ \\t]*")
-
-
1
def self.call(template)
-
new.call(template)
-
end
-
-
1
def supports_streaming?
-
true
-
end
-
-
1
def handles_encoding?
-
true
-
end
-
-
1
def call(template)
-
if template.source.encoding_aware?
-
# First, convert to BINARY, so in case the encoding is
-
# wrong, we can still find an encoding tag
-
# (<%# encoding %>) inside the String using a regular
-
# expression
-
template_source = template.source.dup.force_encoding("BINARY")
-
-
erb = template_source.gsub(ENCODING_TAG, '')
-
encoding = $2
-
-
erb.force_encoding valid_encoding(template.source.dup, encoding)
-
-
# Always make sure we return a String in the default_internal
-
erb.encode!
-
else
-
erb = template.source.dup
-
end
-
-
self.class.erb_implementation.new(
-
erb,
-
:trim => (self.class.erb_trim_mode == "-")
-
).src
-
end
-
-
1
private
-
-
1
def valid_encoding(string, encoding)
-
# If a magic encoding comment was found, tag the
-
# String with this encoding. This is for a case
-
# where the original String was assumed to be,
-
# for instance, UTF-8, but a magic comment
-
# proved otherwise
-
string.force_encoding(encoding) if encoding
-
-
# If the String is valid, return the encoding we found
-
return string.encoding if string.valid_encoding?
-
-
# Otherwise, raise an exception
-
raise WrongEncodingError.new(string, string.encoding)
-
end
-
end
-
end
-
end
-
end
-
1
require "pathname"
-
1
require "active_support/core_ext/class"
-
1
require "action_view/template"
-
-
1
module ActionView
-
# = Action View Resolver
-
1
class Resolver
-
# Keeps all information about view path and builds virtual path.
-
1
class Path < String
-
1
attr_reader :name, :prefix, :partial, :virtual
-
1
alias_method :partial?, :partial
-
-
1
def self.build(name, prefix, partial)
-
virtual = ""
-
virtual << "#{prefix}/" unless prefix.empty?
-
virtual << (partial ? "_#{name}" : name)
-
new name, prefix, partial, virtual
-
end
-
-
1
def initialize(name, prefix, partial, virtual)
-
@name, @prefix, @partial = name, prefix, partial
-
super(virtual)
-
end
-
end
-
-
1
cattr_accessor :caching
-
1
self.caching = true
-
-
1
class << self
-
1
alias :caching? :caching
-
end
-
-
1
def initialize
-
1
@cached = Hash.new { |h1,k1| h1[k1] = Hash.new { |h2,k2|
-
h2[k2] = Hash.new { |h3,k3| h3[k3] = Hash.new { |h4,k4| h4[k4] = {} } } } }
-
end
-
-
1
def clear_cache
-
@cached.clear
-
end
-
-
# Normalizes the arguments and passes it on to find_template.
-
1
def find_all(name, prefix=nil, partial=false, details={}, key=nil, locals=[])
-
cached(key, [name, prefix, partial], details, locals) do
-
find_templates(name, prefix, partial, details)
-
end
-
end
-
-
1
private
-
-
1
delegate :caching?, :to => "self.class"
-
-
# This is what child classes implement. No defaults are needed
-
# because Resolver guarantees that the arguments are present and
-
# normalized.
-
1
def find_templates(name, prefix, partial, details)
-
raise NotImplementedError, "Subclasses must implement a find_templates(name, prefix, partial, details) method"
-
end
-
-
# Helpers that builds a path. Useful for building virtual paths.
-
1
def build_path(name, prefix, partial)
-
Path.build(name, prefix, partial)
-
end
-
-
# Handles templates caching. If a key is given and caching is on
-
# always check the cache before hitting the resolver. Otherwise,
-
# it always hits the resolver but check if the resolver is fresher
-
# before returning it.
-
1
def cached(key, path_info, details, locals) #:nodoc:
-
name, prefix, partial = path_info
-
locals = sort_locals(locals)
-
-
if key && caching?
-
@cached[key][name][prefix][partial][locals] ||= decorate(yield, path_info, details, locals)
-
else
-
fresh = decorate(yield, path_info, details, locals)
-
return fresh unless key
-
-
scope = @cached[key][name][prefix][partial]
-
cache = scope[locals]
-
mtime = cache && cache.map(&:updated_at).max
-
-
if !mtime || fresh.empty? || fresh.any? { |t| t.updated_at > mtime }
-
scope[locals] = fresh
-
else
-
cache
-
end
-
end
-
end
-
-
# Ensures all the resolver information is set in the template.
-
1
def decorate(templates, path_info, details, locals) #:nodoc:
-
cached = nil
-
templates.each do |t|
-
t.locals = locals
-
t.formats = details[:formats] || [:html] if t.formats.empty?
-
t.virtual_path ||= (cached ||= build_path(*path_info))
-
end
-
end
-
-
1
if :symbol.respond_to?("<=>")
-
1
def sort_locals(locals) #:nodoc:
-
locals.sort.freeze
-
end
-
else
-
def sort_locals(locals) #:nodoc:
-
locals = locals.map{ |l| l.to_s }
-
locals.sort!
-
locals.freeze
-
end
-
end
-
end
-
-
# An abstract class that implements a Resolver with path semantics.
-
1
class PathResolver < Resolver #:nodoc:
-
1
EXTENSIONS = [:locale, :formats, :handlers]
-
1
DEFAULT_PATTERN = ":prefix/:action{.:locale,}{.:formats,}{.:handlers,}"
-
-
1
def initialize(pattern=nil)
-
1
@pattern = pattern || DEFAULT_PATTERN
-
1
super()
-
end
-
-
1
private
-
-
1
def find_templates(name, prefix, partial, details)
-
path = Path.build(name, prefix, partial)
-
query(path, details, details[:formats])
-
end
-
-
1
def query(path, details, formats)
-
query = build_query(path, details)
-
templates = []
-
sanitizer = Hash.new { |h,k| h[k] = Dir["#{File.dirname(k)}/*"] }
-
-
Dir[query].each do |p|
-
next if File.directory?(p) || !sanitizer[p].include?(p)
-
-
handler, format = extract_handler_and_format(p, formats)
-
contents = File.open(p, "rb") { |io| io.read }
-
-
templates << Template.new(contents, File.expand_path(p), handler,
-
:virtual_path => path.virtual, :format => format, :updated_at => mtime(p))
-
end
-
-
templates
-
end
-
-
# Helper for building query glob string based on resolver's pattern.
-
1
def build_query(path, details)
-
query = @pattern.dup
-
-
prefix = path.prefix.empty? ? "" : "#{escape_entry(path.prefix)}\\1"
-
query.gsub!(/\:prefix(\/)?/, prefix)
-
-
partial = escape_entry(path.partial? ? "_#{path.name}" : path.name)
-
query.gsub!(/\:action/, partial)
-
-
details.each do |ext, variants|
-
query.gsub!(/\:#{ext}/, "{#{variants.compact.uniq.join(',')}}")
-
end
-
-
File.expand_path(query, @path)
-
end
-
-
1
def escape_entry(entry)
-
entry.gsub(/[*?{}\[\]]/, '\\\\\\&')
-
end
-
-
# Returns the file mtime from the filesystem.
-
1
def mtime(p)
-
File.stat(p).mtime
-
end
-
-
# Extract handler and formats from path. If a format cannot be a found neither
-
# from the path, or the handler, we should return the array of formats given
-
# to the resolver.
-
1
def extract_handler_and_format(path, default_formats)
-
pieces = File.basename(path).split(".")
-
pieces.shift
-
handler = Template.handler_for_extension(pieces.pop)
-
format = pieces.last && Mime[pieces.last]
-
[handler, format]
-
end
-
end
-
-
# A resolver that loads files from the filesystem. It allows to set your own
-
# resolving pattern. Such pattern can be a glob string supported by some variables.
-
#
-
# ==== Examples
-
#
-
# Default pattern, loads views the same way as previous versions of rails, eg. when you're
-
# looking for `users/new` it will produce query glob: `users/new{.{en},}{.{html,js},}{.{erb,haml},}`
-
#
-
# FileSystemResolver.new("/path/to/views", ":prefix/:action{.:locale,}{.:formats,}{.:handlers,}")
-
#
-
# This one allows you to keep files with different formats in seperated subdirectories,
-
# eg. `users/new.html` will be loaded from `users/html/new.erb` or `users/new.html.erb`,
-
# `users/new.js` from `users/js/new.erb` or `users/new.js.erb`, etc.
-
#
-
# FileSystemResolver.new("/path/to/views", ":prefix/{:formats/,}:action{.:locale,}{.:formats,}{.:handlers,}")
-
#
-
# If you don't specify pattern then the default will be used.
-
#
-
# In order to use any of the customized resolvers above in a Rails application, you just need
-
# to configure ActionController::Base.view_paths in an initializer, for example:
-
#
-
# ActionController::Base.view_paths = FileSystemResolver.new(
-
# Rails.root.join("app/views"),
-
# ":prefix{/:locale}/:action{.:formats,}{.:handlers,}"
-
# )
-
#
-
# ==== Pattern format and variables
-
#
-
# Pattern have to be a valid glob string, and it allows you to use the
-
# following variables:
-
#
-
# * <tt>:prefix</tt> - usualy the controller path
-
# * <tt>:action</tt> - name of the action
-
# * <tt>:locale</tt> - possible locale versions
-
# * <tt>:formats</tt> - possible request formats (for example html, json, xml...)
-
# * <tt>:handlers</tt> - possible handlers (for example erb, haml, builder...)
-
#
-
1
class FileSystemResolver < PathResolver
-
1
def initialize(path, pattern=nil)
-
1
raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
-
1
super(pattern)
-
1
@path = File.expand_path(path)
-
end
-
-
1
def to_s
-
@path.to_s
-
end
-
1
alias :to_path :to_s
-
-
1
def eql?(resolver)
-
self.class.equal?(resolver.class) && to_path == resolver.to_path
-
end
-
1
alias :== :eql?
-
end
-
-
# An Optimized resolver for Rails' most common case.
-
1
class OptimizedFileSystemResolver < FileSystemResolver #:nodoc:
-
1
def build_query(path, details)
-
exts = EXTENSIONS.map { |ext| details[ext] }
-
query = escape_entry(File.join(@path, path))
-
-
exts.each do |ext|
-
query << "{"
-
ext.compact.uniq.each { |e| query << ".#{e}," }
-
query << "}"
-
end
-
-
query
-
end
-
end
-
-
# The same as FileSystemResolver but does not allow templates to store
-
# a virtual path since it is invalid for such resolvers.
-
1
class FallbackFileSystemResolver < FileSystemResolver #:nodoc:
-
1
def self.instances
-
[new(""), new("/")]
-
end
-
-
1
def decorate(*)
-
super.each { |t| t.virtual_path = nil }
-
end
-
end
-
end
-
1
module Sprockets
-
1
module Helpers
-
1
autoload :RailsHelper, "sprockets/helpers/rails_helper"
-
end
-
end
-
1
require "action_view"
-
-
1
module Sprockets
-
1
module Helpers
-
1
module RailsHelper
-
1
extend ActiveSupport::Concern
-
1
include ActionView::Helpers::AssetTagHelper
-
-
1
def asset_paths
-
@asset_paths ||= begin
-
config = self.config if respond_to?(:config)
-
config ||= Rails.application.config
-
controller = self.controller if respond_to?(:controller)
-
paths = RailsHelper::AssetPaths.new(config, controller)
-
paths.asset_environment = asset_environment
-
paths.asset_prefix = asset_prefix
-
paths.asset_digests = asset_digests
-
paths
-
end
-
end
-
-
1
def javascript_include_tag(*sources)
-
options = sources.extract_options!
-
debug = options.key?(:debug) ? options.delete(:debug) : debug_assets?
-
body = options.key?(:body) ? options.delete(:body) : false
-
-
sources.collect do |source|
-
if debug && asset = asset_paths.asset_for(source, 'js')
-
asset.to_a.map { |dep|
-
javascript_include_tag(dep, :debug => false, :body => true)
-
}.join("\n").html_safe
-
else
-
tag_options = {
-
'type' => "text/javascript",
-
'src' => asset_path(source, 'js', body)
-
}.merge(options.stringify_keys)
-
-
content_tag 'script', "", tag_options
-
end
-
end.join("\n").html_safe
-
end
-
-
1
def stylesheet_link_tag(*sources)
-
options = sources.extract_options!
-
debug = options.key?(:debug) ? options.delete(:debug) : debug_assets?
-
body = options.key?(:body) ? options.delete(:body) : false
-
media = options.key?(:media) ? options.delete(:media) : "screen"
-
-
sources.collect do |source|
-
if debug && asset = asset_paths.asset_for(source, 'css')
-
asset.to_a.map { |dep|
-
stylesheet_link_tag(dep, :media => media, :debug => false, :body => true)
-
}.join("\n").html_safe
-
else
-
tag_options = {
-
'rel' => "stylesheet",
-
'type' => "text/css",
-
'media' => media,
-
'href' => asset_path(source, 'css', body, :request)
-
}.merge(options.stringify_keys)
-
-
tag 'link', tag_options
-
end
-
end.join("\n").html_safe
-
end
-
-
1
def asset_path(source, default_ext = nil, body = false, protocol = nil)
-
source = source.logical_path if source.respond_to?(:logical_path)
-
path = asset_paths.compute_public_path(source, 'assets', default_ext, true, protocol)
-
body ? "#{path}?body=1" : path
-
end
-
-
1
private
-
1
def debug_assets?
-
begin
-
Rails.application.config.assets.compile &&
-
(Rails.application.config.assets.debug ||
-
params[:debug_assets] == '1' ||
-
params[:debug_assets] == 'true')
-
rescue NoMethodError
-
false
-
end
-
end
-
-
# Override to specify an alternative prefix for asset path generation.
-
# When combined with a custom +asset_environment+, this can be used to
-
# implement themes that can take advantage of the asset pipeline.
-
#
-
# If you only want to change where the assets are mounted, refer to
-
# +config.assets.prefix+ instead.
-
1
def asset_prefix
-
Rails.application.config.assets.prefix
-
end
-
-
1
def asset_digests
-
Rails.application.config.assets.digests
-
end
-
-
# Override to specify an alternative asset environment for asset
-
# path generation. The environment should already have been mounted
-
# at the prefix returned by +asset_prefix+.
-
1
def asset_environment
-
Rails.application.assets
-
end
-
-
1
class AssetPaths < ::ActionView::AssetPaths #:nodoc:
-
1
attr_accessor :asset_environment, :asset_prefix, :asset_digests
-
-
1
class AssetNotPrecompiledError < StandardError; end
-
-
1
def compute_public_path(source, dir, ext=nil, include_host=true, protocol=nil)
-
super(source, asset_prefix, ext, include_host, protocol)
-
end
-
-
# Return the filesystem path for the source
-
1
def compute_source_path(source, ext)
-
asset_for(source, ext)
-
end
-
-
1
def asset_for(source, ext)
-
source = source.to_s
-
return nil if is_uri?(source)
-
source = rewrite_extension(source, nil, ext)
-
asset_environment[source]
-
end
-
-
1
def digest_for(logical_path)
-
if asset_digests && (digest = asset_digests[logical_path])
-
return digest
-
end
-
-
if Rails.application.config.assets.compile
-
if asset = asset_environment[logical_path]
-
return asset.digest_path
-
end
-
return logical_path
-
else
-
raise AssetNotPrecompiledError.new("#{logical_path} isn't precompiled")
-
end
-
end
-
-
1
def rewrite_asset_path(source, dir)
-
if source[0] == ?/
-
source
-
else
-
source = digest_for(source) if Rails.application.config.assets.digest
-
source = File.join(dir, source)
-
source = "/#{source}" unless source =~ /^\//
-
source
-
end
-
end
-
-
1
def rewrite_extension(source, dir, ext)
-
if ext && File.extname(source).empty?
-
"#{source}.#{ext}"
-
else
-
source
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module Sprockets
-
1
autoload :Helpers, "sprockets/helpers"
-
1
autoload :LazyCompressor, "sprockets/compressors"
-
1
autoload :NullCompressor, "sprockets/compressors"
-
-
# TODO: Get rid of config.assets.enabled
-
1
class Railtie < ::Rails::Railtie
-
1
config.default_asset_host_protocol = :relative
-
-
1
rake_tasks do
-
load "sprockets/assets.rake"
-
end
-
-
1
initializer "sprockets.environment" do |app|
-
1
config = app.config
-
1
next unless config.assets.enabled
-
-
1
require 'sprockets'
-
-
1
app.assets = Sprockets::Environment.new(app.root.to_s) do |env|
-
1
env.logger = ::Rails.logger
-
1
env.version = ::Rails.env + "-#{config.assets.version}"
-
-
1
if config.assets.cache_store != false
-
1
env.cache = ActiveSupport::Cache.lookup_store(config.assets.cache_store) || ::Rails.cache
-
end
-
end
-
-
1
if config.assets.manifest
-
1
path = File.join(config.assets.manifest, "manifest.yml")
-
else
-
path = File.join(Rails.public_path, config.assets.prefix, "manifest.yml")
-
end
-
-
1
if File.exist?(path)
-
config.assets.digests = YAML.load_file(path)
-
end
-
-
1
ActiveSupport.on_load(:action_view) do
-
1
include ::Sprockets::Helpers::RailsHelper
-
-
1
app.assets.context_class.instance_eval do
-
1
include ::Sprockets::Helpers::RailsHelper
-
end
-
end
-
end
-
-
# We need to configure this after initialization to ensure we collect
-
# paths from all engines. This hook is invoked exactly before routes
-
# are compiled, and so that other Railties have an opportunity to
-
# register compressors.
-
1
config.after_initialize do |app|
-
1
next unless app.assets
-
1
config = app.config
-
-
6
config.assets.paths.each { |path| app.assets.append_path(path) }
-
-
1
if config.assets.compress
-
# temporarily hardcode default JS compressor to uglify. Soon, it will work
-
# the same as SCSS, where a default plugin sets the default.
-
unless config.assets.js_compressor == false
-
app.assets.js_compressor = LazyCompressor.new { expand_js_compressor(config.assets.js_compressor || :uglifier) }
-
end
-
-
unless config.assets.css_compressor == false
-
app.assets.css_compressor = LazyCompressor.new { expand_css_compressor(config.assets.css_compressor) }
-
end
-
end
-
-
1
app.routes.prepend do
-
1
mount app.assets => config.assets.prefix
-
end
-
-
1
if config.action_controller.perform_caching
-
app.assets = app.assets.index
-
end
-
end
-
-
1
protected
-
1
def expand_js_compressor(sym)
-
case sym
-
when :closure
-
require 'closure-compiler'
-
Closure::Compiler.new
-
when :uglifier
-
require 'uglifier'
-
Uglifier.new
-
when :yui
-
require 'yui/compressor'
-
YUI::JavaScriptCompressor.new
-
else
-
sym
-
end
-
end
-
-
1
def expand_css_compressor(sym)
-
case sym
-
when :yui
-
require 'yui/compressor'
-
YUI::CssCompressor.new
-
else
-
sym
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/core_ext/class/attribute'
-
-
1
module ActiveModel
-
1
class MissingAttributeError < NoMethodError
-
end
-
# == Active Model Attribute Methods
-
#
-
# <tt>ActiveModel::AttributeMethods</tt> provides a way to add prefixes and suffixes
-
# to your methods as well as handling the creation of Active Record like class methods
-
# such as +table_name+.
-
#
-
# The requirements to implement ActiveModel::AttributeMethods are to:
-
#
-
# * <tt>include ActiveModel::AttributeMethods</tt> in your object
-
# * Call each Attribute Method module method you want to add, such as
-
# attribute_method_suffix or attribute_method_prefix
-
# * Call <tt>define_attribute_methods</tt> after the other methods are
-
# called.
-
# * Define the various generic +_attribute+ methods that you have declared
-
#
-
# A minimal implementation could be:
-
#
-
# class Person
-
# include ActiveModel::AttributeMethods
-
#
-
# attribute_method_affix :prefix => 'reset_', :suffix => '_to_default!'
-
# attribute_method_suffix '_contrived?'
-
# attribute_method_prefix 'clear_'
-
# define_attribute_methods ['name']
-
#
-
# attr_accessor :name
-
#
-
# private
-
#
-
# def attribute_contrived?(attr)
-
# true
-
# end
-
#
-
# def clear_attribute(attr)
-
# send("#{attr}=", nil)
-
# end
-
#
-
# def reset_attribute_to_default!(attr)
-
# send("#{attr}=", "Default Name")
-
# end
-
# end
-
#
-
# Note that whenever you include ActiveModel::AttributeMethods in your class,
-
# it requires you to implement an <tt>attributes</tt> method which returns a hash
-
# with each attribute name in your model as hash key and the attribute value as
-
# hash value.
-
#
-
# Hash keys must be strings.
-
#
-
1
module AttributeMethods
-
1
extend ActiveSupport::Concern
-
-
1
COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?=]?\z/
-
-
1
included do
-
1
class_attribute :attribute_method_matchers, :instance_writer => false
-
1
self.attribute_method_matchers = []
-
end
-
-
1
module ClassMethods
-
# Defines an "attribute" method (like +inheritance_column+ or +table_name+).
-
# A new (class) method will be created with the given name. If a value is
-
# specified, the new method will return that value (as a string).
-
# Otherwise, the given block will be used to compute the value of the
-
# method.
-
#
-
# The original method will be aliased, with the new name being prefixed
-
# with "original_". This allows the new method to access the original
-
# value.
-
#
-
# Example:
-
#
-
# class Person
-
#
-
# include ActiveModel::AttributeMethods
-
#
-
# cattr_accessor :primary_key
-
# cattr_accessor :inheritance_column
-
#
-
# define_attr_method :primary_key, "sysid"
-
# define_attr_method( :inheritance_column ) do
-
# original_inheritance_column + "_id"
-
# end
-
#
-
# end
-
#
-
# Provides you with:
-
#
-
# AttributePerson.primary_key
-
# # => "sysid"
-
# AttributePerson.inheritance_column = 'address'
-
# AttributePerson.inheritance_column
-
# # => 'address_id'
-
1
def define_attr_method(name, value=nil, &block)
-
3
sing = singleton_class
-
3
sing.class_eval <<-eorb, __FILE__, __LINE__ + 1
-
if method_defined?('original_#{name}')
-
undef :'original_#{name}'
-
end
-
alias_method :'original_#{name}', :'#{name}'
-
eorb
-
3
if block_given?
-
sing.send :define_method, name, &block
-
else
-
# If we can compile the method name, do it. Otherwise use define_method.
-
# This is an important *optimization*, please don't change it. define_method
-
# has slower dispatch and consumes more memory.
-
3
if name =~ COMPILABLE_REGEXP
-
3
sing.class_eval <<-RUBY, __FILE__, __LINE__ + 1
-
def #{name}; #{value.nil? ? 'nil' : value.to_s.inspect}; end
-
RUBY
-
else
-
value = value.to_s if value
-
sing.send(:define_method, name) { value }
-
end
-
end
-
end
-
-
# Declares a method available for all attributes with the given prefix.
-
# Uses +method_missing+ and <tt>respond_to?</tt> to rewrite the method.
-
#
-
# #{prefix}#{attr}(*args, &block)
-
#
-
# to
-
#
-
# #{prefix}attribute(#{attr}, *args, &block)
-
#
-
# An instance method <tt>#{prefix}attribute</tt> must exist and accept
-
# at least the +attr+ argument.
-
#
-
# For example:
-
#
-
# class Person
-
#
-
# include ActiveModel::AttributeMethods
-
# attr_accessor :name
-
# attribute_method_prefix 'clear_'
-
# define_attribute_methods [:name]
-
#
-
# private
-
#
-
# def clear_attribute(attr)
-
# send("#{attr}=", nil)
-
# end
-
# end
-
#
-
# person = Person.new
-
# person.name = "Bob"
-
# person.name # => "Bob"
-
# person.clear_name
-
# person.name # => nil
-
1
def attribute_method_prefix(*prefixes)
-
self.attribute_method_matchers += prefixes.map { |prefix| AttributeMethodMatcher.new :prefix => prefix }
-
undefine_attribute_methods
-
end
-
-
# Declares a method available for all attributes with the given suffix.
-
# Uses +method_missing+ and <tt>respond_to?</tt> to rewrite the method.
-
#
-
# #{attr}#{suffix}(*args, &block)
-
#
-
# to
-
#
-
# attribute#{suffix}(#{attr}, *args, &block)
-
#
-
# An <tt>attribute#{suffix}</tt> instance method must exist and accept at least
-
# the +attr+ argument.
-
#
-
# For example:
-
#
-
# class Person
-
#
-
# include ActiveModel::AttributeMethods
-
# attr_accessor :name
-
# attribute_method_suffix '_short?'
-
# define_attribute_methods [:name]
-
#
-
# private
-
#
-
# def attribute_short?(attr)
-
# send(attr).length < 5
-
# end
-
# end
-
#
-
# person = Person.new
-
# person.name = "Bob"
-
# person.name # => "Bob"
-
# person.name_short? # => true
-
1
def attribute_method_suffix(*suffixes)
-
13
self.attribute_method_matchers += suffixes.map { |suffix| AttributeMethodMatcher.new :suffix => suffix }
-
5
undefine_attribute_methods
-
end
-
-
# Declares a method available for all attributes with the given prefix
-
# and suffix. Uses +method_missing+ and <tt>respond_to?</tt> to rewrite
-
# the method.
-
#
-
# #{prefix}#{attr}#{suffix}(*args, &block)
-
#
-
# to
-
#
-
# #{prefix}attribute#{suffix}(#{attr}, *args, &block)
-
#
-
# An <tt>#{prefix}attribute#{suffix}</tt> instance method must exist and
-
# accept at least the +attr+ argument.
-
#
-
# For example:
-
#
-
# class Person
-
#
-
# include ActiveModel::AttributeMethods
-
# attr_accessor :name
-
# attribute_method_affix :prefix => 'reset_', :suffix => '_to_default!'
-
# define_attribute_methods [:name]
-
#
-
# private
-
#
-
# def reset_attribute_to_default!(attr)
-
# ...
-
# end
-
# end
-
#
-
# person = Person.new
-
# person.name # => 'Gem'
-
# person.reset_name_to_default!
-
# person.name # => 'Gemma'
-
1
def attribute_method_affix(*affixes)
-
2
self.attribute_method_matchers += affixes.map { |affix| AttributeMethodMatcher.new :prefix => affix[:prefix], :suffix => affix[:suffix] }
-
1
undefine_attribute_methods
-
end
-
-
1
def alias_attribute(new_name, old_name)
-
attribute_method_matchers.each do |matcher|
-
matcher_new = matcher.method_name(new_name).to_s
-
matcher_old = matcher.method_name(old_name).to_s
-
-
if matcher_new =~ COMPILABLE_REGEXP && matcher_old =~ COMPILABLE_REGEXP
-
module_eval <<-RUBY, __FILE__, __LINE__ + 1
-
def #{matcher_new}(*args)
-
send(:#{matcher_old}, *args)
-
end
-
RUBY
-
else
-
define_method(matcher_new) do |*args|
-
send(matcher_old, *args)
-
end
-
end
-
end
-
end
-
-
# Declares the attributes that should be prefixed and suffixed by
-
# ActiveModel::AttributeMethods.
-
#
-
# To use, pass in an array of attribute names (as strings or symbols),
-
# be sure to declare +define_attribute_methods+ after you define any
-
# prefix, suffix or affix methods, or they will not hook in.
-
#
-
# class Person
-
#
-
# include ActiveModel::AttributeMethods
-
# attr_accessor :name, :age, :address
-
# attribute_method_prefix 'clear_'
-
#
-
# # Call to define_attribute_methods must appear after the
-
# # attribute_method_prefix, attribute_method_suffix or
-
# # attribute_method_affix declares.
-
# define_attribute_methods [:name, :age, :address]
-
#
-
# private
-
#
-
# def clear_attribute(attr)
-
# ...
-
# end
-
# end
-
1
def define_attribute_methods(attr_names)
-
8
attr_names.each { |attr_name| define_attribute_method(attr_name) }
-
end
-
-
1
def define_attribute_method(attr_name)
-
7
attribute_method_matchers.each do |matcher|
-
63
unless instance_method_already_implemented?(matcher.method_name(attr_name))
-
63
generate_method = "define_method_#{matcher.prefix}attribute#{matcher.suffix}"
-
-
63
if respond_to?(generate_method)
-
14
send(generate_method, attr_name)
-
else
-
49
method_name = matcher.method_name(attr_name)
-
-
49
generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
-
if method_defined?('#{method_name}')
-
undef :'#{method_name}'
-
end
-
RUBY
-
-
49
if method_name.to_s =~ COMPILABLE_REGEXP
-
49
generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
-
def #{method_name}(*args)
-
send(:#{matcher.method_missing_target}, '#{attr_name}', *args)
-
end
-
RUBY
-
else
-
generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
-
define_method('#{method_name}') do |*args|
-
send('#{matcher.method_missing_target}', '#{attr_name}', *args)
-
end
-
RUBY
-
end
-
end
-
end
-
end
-
end
-
-
# Removes all the previously dynamically defined methods from the class
-
1
def undefine_attribute_methods
-
6
generated_attribute_methods.module_eval do
-
6
instance_methods.each { |m| undef_method(m) }
-
end
-
end
-
-
# Returns true if the attribute methods defined have been generated.
-
1
def generated_attribute_methods #:nodoc:
-
@generated_attribute_methods ||= begin
-
2
mod = Module.new
-
2
include mod
-
2
mod
-
118
end
-
end
-
-
1
protected
-
1
def instance_method_already_implemented?(method_name)
-
method_defined?(method_name)
-
end
-
-
1
private
-
1
class AttributeMethodMatcher
-
1
attr_reader :prefix, :suffix, :method_missing_target
-
-
1
AttributeMethodMatch = Struct.new(:target, :attr_name)
-
-
1
def initialize(options = {})
-
9
options.symbolize_keys!
-
9
@prefix, @suffix = options[:prefix] || '', options[:suffix] || ''
-
9
@regex = /^(#{Regexp.escape(@prefix)})(.+?)(#{Regexp.escape(@suffix)})$/
-
9
@method_missing_target = "#{@prefix}attribute#{@suffix}"
-
9
@method_name = "#{prefix}%s#{suffix}"
-
end
-
-
1
def match(method_name)
-
if @regex =~ method_name
-
AttributeMethodMatch.new(method_missing_target, $2)
-
else
-
nil
-
end
-
end
-
-
1
def method_name(attr_name)
-
112
@method_name % attr_name
-
end
-
end
-
end
-
-
# Allows access to the object attributes, which are held in the
-
# <tt>@attributes</tt> hash, as though they were first-class methods. So a
-
# Person class with a name attribute can use Person#name and Person#name=
-
# and never directly use the attributes hash -- except for multiple assigns
-
# with ActiveRecord#attributes=. A Milestone class can also ask
-
# Milestone#completed? to test that the completed attribute is not +nil+
-
# or 0.
-
#
-
# It's also possible to instantiate related objects, so a Client class
-
# belonging to the clients table with a +master_id+ foreign key can
-
# instantiate master through Client#master.
-
1
def method_missing(method_id, *args, &block)
-
method_name = method_id.to_s
-
if match = match_attribute_method?(method_name)
-
guard_private_attribute_method!(method_name, args)
-
return __send__(match.target, match.attr_name, *args, &block)
-
end
-
super
-
end
-
-
# A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
-
# <tt>person.respond_to?(:name=)</tt>, and <tt>person.respond_to?(:name?)</tt>
-
# which will all return +true+.
-
1
alias :respond_to_without_attributes? :respond_to?
-
1
def respond_to?(method, include_private_methods = false)
-
18
if super
-
18
return true
-
elsif !include_private_methods && super(method, true)
-
# If we're here then we haven't found among non-private methods
-
# but found among all methods. Which means that the given method is private.
-
return false
-
elsif match_attribute_method?(method.to_s)
-
return true
-
end
-
super
-
end
-
-
1
protected
-
1
def attribute_method?(attr_name)
-
attributes.include?(attr_name)
-
end
-
-
1
private
-
# Returns a struct representing the matching attribute method.
-
# The struct's attributes are prefix, base and suffix.
-
1
def match_attribute_method?(method_name)
-
self.class.attribute_method_matchers.each do |method|
-
if (match = method.match(method_name)) && attribute_method?(match.attr_name)
-
return match
-
end
-
end
-
nil
-
end
-
-
# prevent method_missing from calling private methods with #send
-
1
def guard_private_attribute_method!(method_name, args)
-
if self.class.private_method_defined?(method_name)
-
raise NoMethodError.new("Attempt to call private method `#{method_name}'", method_name, args)
-
end
-
end
-
-
1
def missing_attribute(attr_name, stack)
-
raise ActiveModel::MissingAttributeError, "missing attribute: #{attr_name}", stack
-
end
-
end
-
end
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_support/callbacks'
-
-
1
module ActiveModel
-
# == Active Model Callbacks
-
#
-
# Provides an interface for any class to have Active Record like callbacks.
-
#
-
# Like the Active Record methods, the callback chain is aborted as soon as
-
# one of the methods in the chain returns false.
-
#
-
# First, extend ActiveModel::Callbacks from the class you are creating:
-
#
-
# class MyModel
-
# extend ActiveModel::Callbacks
-
# end
-
#
-
# Then define a list of methods that you want callbacks attached to:
-
#
-
# define_model_callbacks :create, :update
-
#
-
# This will provide all three standard callbacks (before, around and after) for
-
# both the :create and :update methods. To implement, you need to wrap the methods
-
# you want callbacks on in a block so that the callbacks get a chance to fire:
-
#
-
# def create
-
# run_callbacks :create do
-
# # Your create action methods here
-
# end
-
# end
-
#
-
# Then in your class, you can use the +before_create+, +after_create+ and +around_create+
-
# methods, just as you would in an Active Record module.
-
#
-
# before_create :action_before_create
-
#
-
# def action_before_create
-
# # Your code here
-
# end
-
#
-
# You can choose not to have all three callbacks by passing a hash to the
-
# define_model_callbacks method.
-
#
-
# define_model_callbacks :create, :only => :after, :before
-
#
-
# Would only create the after_create and before_create callback methods in your
-
# class.
-
1
module Callbacks
-
1
def self.extended(base)
-
1
base.class_eval do
-
1
include ActiveSupport::Callbacks
-
end
-
end
-
-
# define_model_callbacks accepts the same options define_callbacks does, in case
-
# you want to overwrite a default. Besides that, it also accepts an :only option,
-
# where you can choose if you want all types (before, around or after) or just some.
-
#
-
# define_model_callbacks :initializer, :only => :after
-
#
-
# Note, the <tt>:only => <type></tt> hash will apply to all callbacks defined on
-
# that method call. To get around this you can call the define_model_callbacks
-
# method as many times as you need.
-
#
-
# define_model_callbacks :create, :only => :after
-
# define_model_callbacks :update, :only => :before
-
# define_model_callbacks :destroy, :only => :around
-
#
-
# Would create +after_create+, +before_update+ and +around_destroy+ methods only.
-
#
-
# You can pass in a class to before_<type>, after_<type> and around_<type>, in which
-
# case the callback will call that class's <action>_<type> method passing the object
-
# that the callback is being called on.
-
#
-
# class MyModel
-
# extend ActiveModel::Callbacks
-
# define_model_callbacks :create
-
#
-
# before_create AnotherClass
-
# end
-
#
-
# class AnotherClass
-
# def self.before_create( obj )
-
# # obj is the MyModel instance that the callback is being called on
-
# end
-
# end
-
#
-
1
def define_model_callbacks(*callbacks)
-
2
options = callbacks.extract_options!
-
2
options = {
-
:terminator => "result == false",
-
:scope => [:kind, :name],
-
:only => [:before, :around, :after]
-
}.merge(options)
-
-
2
types = Array.wrap(options.delete(:only))
-
-
2
callbacks.each do |callback|
-
7
define_callbacks(callback, options)
-
-
7
types.each do |type|
-
15
send("_define_#{type}_model_callback", self, callback)
-
end
-
end
-
end
-
-
1
def _define_before_model_callback(klass, callback) #:nodoc:
-
4
klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
-
def self.before_#{callback}(*args, &block)
-
set_callback(:#{callback}, :before, *args, &block)
-
end
-
CALLBACK
-
end
-
-
1
def _define_around_model_callback(klass, callback) #:nodoc:
-
4
klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
-
def self.around_#{callback}(*args, &block)
-
set_callback(:#{callback}, :around, *args, &block)
-
end
-
CALLBACK
-
end
-
-
1
def _define_after_model_callback(klass, callback) #:nodoc:
-
7
klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
-
def self.after_#{callback}(*args, &block)
-
options = args.extract_options!
-
options[:prepend] = true
-
options[:if] = Array.wrap(options[:if]) << "!halted && value != false"
-
set_callback(:#{callback}, :after, *(args << options), &block)
-
end
-
CALLBACK
-
end
-
end
-
end
-
1
module ActiveModel
-
# == Active Model Conversions
-
#
-
# Handles default conversions: to_model, to_key and to_param.
-
#
-
# Let's take for example this non persisted object.
-
#
-
# class ContactMessage
-
# include ActiveModel::Conversion
-
#
-
# # ContactMessage are never persisted in the DB
-
# def persisted?
-
# false
-
# end
-
# end
-
#
-
# cm = ContactMessage.new
-
# cm.to_model == self # => true
-
# cm.to_key # => nil
-
# cm.to_param # => nil
-
#
-
1
module Conversion
-
# If your object is already designed to implement all of the Active Model
-
# you can use the default <tt>:to_model</tt> implementation, which simply
-
# returns self.
-
#
-
# If your model does not act like an Active Model object, then you should
-
# define <tt>:to_model</tt> yourself returning a proxy object that wraps
-
# your object with Active Model compliant methods.
-
1
def to_model
-
self
-
end
-
-
# Returns an Enumerable of all key attributes if any is set, regardless
-
# if the object is persisted or not.
-
#
-
# Note the default implementation uses persisted? just because all objects
-
# in Ruby 1.8.x responds to <tt>:id</tt>.
-
1
def to_key
-
persisted? ? [id] : nil
-
end
-
-
# Returns a string representing the object's key suitable for use in URLs,
-
# or nil if <tt>persisted?</tt> is false.
-
1
def to_param
-
persisted? ? to_key.join('-') : nil
-
end
-
end
-
end
-
1
require 'active_model/attribute_methods'
-
1
require 'active_support/hash_with_indifferent_access'
-
1
require 'active_support/core_ext/object/duplicable'
-
-
1
module ActiveModel
-
# == Active Model Dirty
-
#
-
# Provides a way to track changes in your object in the same way as
-
# Active Record does.
-
#
-
# The requirements for implementing ActiveModel::Dirty are:
-
#
-
# * <tt>include ActiveModel::Dirty</tt> in your object
-
# * Call <tt>define_attribute_methods</tt> passing each method you want to
-
# track
-
# * Call <tt>attr_name_will_change!</tt> before each change to the tracked
-
# attribute
-
#
-
# If you wish to also track previous changes on save or update, you need to
-
# add
-
#
-
# @previously_changed = changes
-
#
-
# inside of your save or update method.
-
#
-
# A minimal implementation could be:
-
#
-
# class Person
-
#
-
# include ActiveModel::Dirty
-
#
-
# define_attribute_methods = [:name]
-
#
-
# def name
-
# @name
-
# end
-
#
-
# def name=(val)
-
# name_will_change! unless val == @name
-
# @name = val
-
# end
-
#
-
# def save
-
# @previously_changed = changes
-
# @changed_attributes.clear
-
# end
-
#
-
# end
-
#
-
# == Examples:
-
#
-
# A newly instantiated object is unchanged:
-
# person = Person.find_by_name('Uncle Bob')
-
# person.changed? # => false
-
#
-
# Change the name:
-
# person.name = 'Bob'
-
# person.changed? # => true
-
# person.name_changed? # => true
-
# person.name_was # => 'Uncle Bob'
-
# person.name_change # => ['Uncle Bob', 'Bob']
-
# person.name = 'Bill'
-
# person.name_change # => ['Uncle Bob', 'Bill']
-
#
-
# Save the changes:
-
# person.save
-
# person.changed? # => false
-
# person.name_changed? # => false
-
#
-
# Assigning the same value leaves the attribute unchanged:
-
# person.name = 'Bill'
-
# person.name_changed? # => false
-
# person.name_change # => nil
-
#
-
# Which attributes have changed?
-
# person.name = 'Bob'
-
# person.changed # => ['name']
-
# person.changes # => { 'name' => ['Bill', 'Bob'] }
-
#
-
# If an attribute is modified in-place then make use of <tt>[attribute_name]_will_change!</tt>
-
# to mark that the attribute is changing. Otherwise ActiveModel can't track changes to
-
# in-place attributes.
-
#
-
# person.name_will_change!
-
# person.name << 'y'
-
# person.name_change # => ['Bill', 'Billy']
-
1
module Dirty
-
1
extend ActiveSupport::Concern
-
1
include ActiveModel::AttributeMethods
-
-
1
included do
-
1
attribute_method_suffix '_changed?', '_change', '_will_change!', '_was'
-
1
attribute_method_affix :prefix => 'reset_', :suffix => '!'
-
end
-
-
# Returns true if any attribute have unsaved changes, false otherwise.
-
# person.changed? # => false
-
# person.name = 'bob'
-
# person.changed? # => true
-
1
def changed?
-
!changed_attributes.empty?
-
end
-
-
# List of attributes with unsaved changes.
-
# person.changed # => []
-
# person.name = 'bob'
-
# person.changed # => ['name']
-
1
def changed
-
changed_attributes.keys
-
end
-
-
# Map of changed attrs => [original value, new value].
-
# person.changes # => {}
-
# person.name = 'bob'
-
# person.changes # => { 'name' => ['bill', 'bob'] }
-
1
def changes
-
HashWithIndifferentAccess[changed.map { |attr| [attr, attribute_change(attr)] }]
-
end
-
-
# Map of attributes that were changed when the model was saved.
-
# person.name # => 'bob'
-
# person.name = 'robert'
-
# person.save
-
# person.previous_changes # => {'name' => ['bob, 'robert']}
-
1
def previous_changes
-
@previously_changed
-
end
-
-
# Map of change <tt>attr => original value</tt>.
-
1
def changed_attributes
-
9
@changed_attributes ||= {}
-
end
-
-
1
private
-
-
# Handle <tt>*_changed?</tt> for +method_missing+.
-
1
def attribute_changed?(attr)
-
9
changed_attributes.include?(attr)
-
end
-
-
# Handle <tt>*_change</tt> for +method_missing+.
-
1
def attribute_change(attr)
-
[changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
-
end
-
-
# Handle <tt>*_was</tt> for +method_missing+.
-
1
def attribute_was(attr)
-
attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
-
end
-
-
# Handle <tt>*_will_change!</tt> for +method_missing+.
-
1
def attribute_will_change!(attr)
-
begin
-
value = __send__(attr)
-
value = value.duplicable? ? value.clone : value
-
rescue TypeError, NoMethodError
-
end
-
-
changed_attributes[attr] = value
-
end
-
-
# Handle <tt>reset_*!</tt> for +method_missing+.
-
1
def reset_attribute!(attr)
-
__send__("#{attr}=", changed_attributes[attr]) if attribute_changed?(attr)
-
end
-
end
-
end
-
# -*- coding: utf-8 -*-
-
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_support/core_ext/array/conversions'
-
1
require 'active_support/core_ext/string/inflections'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/hash/reverse_merge'
-
1
require 'active_support/ordered_hash'
-
-
1
module ActiveModel
-
# == Active Model Errors
-
#
-
# Provides a modified +OrderedHash+ that you can include in your object
-
# for handling error messages and interacting with Action Pack helpers.
-
#
-
# A minimal implementation could be:
-
#
-
# class Person
-
#
-
# # Required dependency for ActiveModel::Errors
-
# extend ActiveModel::Naming
-
#
-
# def initialize
-
# @errors = ActiveModel::Errors.new(self)
-
# end
-
#
-
# attr_accessor :name
-
# attr_reader :errors
-
#
-
# def validate!
-
# errors.add(:name, "can not be nil") if name == nil
-
# end
-
#
-
# # The following methods are needed to be minimally implemented
-
#
-
# def read_attribute_for_validation(attr)
-
# send(attr)
-
# end
-
#
-
# def Person.human_attribute_name(attr, options = {})
-
# attr
-
# end
-
#
-
# def Person.lookup_ancestors
-
# [self]
-
# end
-
#
-
# end
-
#
-
# The last three methods are required in your object for Errors to be
-
# able to generate error messages correctly and also handle multiple
-
# languages. Of course, if you extend your object with ActiveModel::Translations
-
# you will not need to implement the last two. Likewise, using
-
# ActiveModel::Validations will handle the validation related methods
-
# for you.
-
#
-
# The above allows you to do:
-
#
-
# p = Person.new
-
# p.validate! # => ["can not be nil"]
-
# p.errors.full_messages # => ["name can not be nil"]
-
# # etc..
-
1
class Errors
-
1
include Enumerable
-
-
1
CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank]
-
-
1
attr_reader :messages
-
-
# Pass in the instance of the object that is using the errors object.
-
#
-
# class Person
-
# def initialize
-
# @errors = ActiveModel::Errors.new(self)
-
# end
-
# end
-
1
def initialize(base)
-
@base = base
-
@messages = ActiveSupport::OrderedHash.new
-
end
-
-
# Clear the messages
-
1
def clear
-
messages.clear
-
end
-
-
# Do the error messages include an error with key +error+?
-
1
def include?(error)
-
(v = messages[error]) && v.any?
-
end
-
-
# Get messages for +key+
-
1
def get(key)
-
messages[key]
-
end
-
-
# Set messages for +key+ to +value+
-
1
def set(key, value)
-
messages[key] = value
-
end
-
-
# When passed a symbol or a name of a method, returns an array of errors
-
# for the method.
-
#
-
# p.errors[:name] # => ["can not be nil"]
-
# p.errors['name'] # => ["can not be nil"]
-
1
def [](attribute)
-
get(attribute.to_sym) || set(attribute.to_sym, [])
-
end
-
-
# Adds to the supplied attribute the supplied error message.
-
#
-
# p.errors[:name] = "must be set"
-
# p.errors[:name] # => ['must be set']
-
1
def []=(attribute, error)
-
self[attribute.to_sym] << error
-
end
-
-
# Iterates through each error key, value pair in the error messages hash.
-
# Yields the attribute and the error for that attribute. If the attribute
-
# has more than one error message, yields once for each error message.
-
#
-
# p.errors.add(:name, "can't be blank")
-
# p.errors.each do |attribute, errors_array|
-
# # Will yield :name and "can't be blank"
-
# end
-
#
-
# p.errors.add(:name, "must be specified")
-
# p.errors.each do |attribute, errors_array|
-
# # Will yield :name and "can't be blank"
-
# # then yield :name and "must be specified"
-
# end
-
1
def each
-
messages.each_key do |attribute|
-
self[attribute].each { |error| yield attribute, error }
-
end
-
end
-
-
# Returns the number of error messages.
-
#
-
# p.errors.add(:name, "can't be blank")
-
# p.errors.size # => 1
-
# p.errors.add(:name, "must be specified")
-
# p.errors.size # => 2
-
1
def size
-
values.flatten.size
-
end
-
-
# Returns all message values
-
1
def values
-
messages.values
-
end
-
-
# Returns all message keys
-
1
def keys
-
messages.keys
-
end
-
-
# Returns an array of error messages, with the attribute name included
-
#
-
# p.errors.add(:name, "can't be blank")
-
# p.errors.add(:name, "must be specified")
-
# p.errors.to_a # => ["name can't be blank", "name must be specified"]
-
1
def to_a
-
full_messages
-
end
-
-
# Returns the number of error messages.
-
# p.errors.add(:name, "can't be blank")
-
# p.errors.count # => 1
-
# p.errors.add(:name, "must be specified")
-
# p.errors.count # => 2
-
1
def count
-
to_a.size
-
end
-
-
# Returns true if no errors are found, false otherwise.
-
1
def empty?
-
all? { |k, v| v && v.empty? }
-
end
-
1
alias_method :blank?, :empty?
-
# Returns an xml formatted representation of the Errors hash.
-
#
-
# p.errors.add(:name, "can't be blank")
-
# p.errors.add(:name, "must be specified")
-
# p.errors.to_xml
-
# # =>
-
# # <?xml version=\"1.0\" encoding=\"UTF-8\"?>
-
# # <errors>
-
# # <error>name can't be blank</error>
-
# # <error>name must be specified</error>
-
# # </errors>
-
1
def to_xml(options={})
-
to_a.to_xml options.reverse_merge(:root => "errors", :skip_types => true)
-
end
-
-
# Returns an ActiveSupport::OrderedHash that can be used as the JSON representation for this object.
-
1
def as_json(options=nil)
-
to_hash
-
end
-
-
1
def to_hash
-
messages.dup
-
end
-
-
# Adds +message+ to the error messages on +attribute+, which will be returned on a call to
-
# <tt>on(attribute)</tt> for the same attribute. More than one error can be added to the same
-
# +attribute+ in which case an array will be returned on a call to <tt>on(attribute)</tt>.
-
# If no +message+ is supplied, <tt>:invalid</tt> is assumed.
-
#
-
# If +message+ is a symbol, it will be translated using the appropriate scope (see +translate_error+).
-
# If +message+ is a proc, it will be called, allowing for things like <tt>Time.now</tt> to be used within an error.
-
1
def add(attribute, message = nil, options = {})
-
message ||= :invalid
-
-
if message.is_a?(Symbol)
-
message = generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
-
elsif message.is_a?(Proc)
-
message = message.call
-
end
-
-
self[attribute] << message
-
end
-
-
# Will add an error message to each of the attributes in +attributes+ that is empty.
-
1
def add_on_empty(attributes, options = {})
-
[attributes].flatten.each do |attribute|
-
value = @base.send(:read_attribute_for_validation, attribute)
-
is_empty = value.respond_to?(:empty?) ? value.empty? : false
-
add(attribute, :empty, options) if value.nil? || is_empty
-
end
-
end
-
-
# Will add an error message to each of the attributes in +attributes+ that is blank (using Object#blank?).
-
1
def add_on_blank(attributes, options = {})
-
[attributes].flatten.each do |attribute|
-
value = @base.send(:read_attribute_for_validation, attribute)
-
add(attribute, :blank, options) if value.blank?
-
end
-
end
-
-
# Returns all the full error messages in an array.
-
#
-
# class Company
-
# validates_presence_of :name, :address, :email
-
# validates_length_of :name, :in => 5..30
-
# end
-
#
-
# company = Company.create(:address => '123 First St.')
-
# company.errors.full_messages # =>
-
# ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Email can't be blank"]
-
1
def full_messages
-
map { |attribute, message|
-
if attribute == :base
-
message
-
else
-
attr_name = attribute.to_s.gsub('.', '_').humanize
-
attr_name = @base.class.human_attribute_name(attribute, :default => attr_name)
-
-
I18n.t(:"errors.format", {
-
:default => "%{attribute} %{message}",
-
:attribute => attr_name,
-
:message => message
-
})
-
end
-
}
-
end
-
-
# Translates an error message in its default scope
-
# (<tt>activemodel.errors.messages</tt>).
-
#
-
# Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>,
-
# if it's not there, it's looked up in <tt>models.MODEL.MESSAGE</tt> and if that is not
-
# there also, it returns the translation of the default message
-
# (e.g. <tt>activemodel.errors.messages.MESSAGE</tt>). The translated model name,
-
# translated attribute name and the value are available for interpolation.
-
#
-
# When using inheritance in your models, it will check all the inherited
-
# models too, but only if the model itself hasn't been found. Say you have
-
# <tt>class Admin < User; end</tt> and you wanted the translation for
-
# the <tt>:blank</tt> error message for the <tt>title</tt> attribute,
-
# it looks for these translations:
-
#
-
# * <tt>activemodel.errors.models.admin.attributes.title.blank</tt>
-
# * <tt>activemodel.errors.models.admin.blank</tt>
-
# * <tt>activemodel.errors.models.user.attributes.title.blank</tt>
-
# * <tt>activemodel.errors.models.user.blank</tt>
-
# * any default you provided through the +options+ hash (in the <tt>activemodel.errors</tt> scope)
-
# * <tt>activemodel.errors.messages.blank</tt>
-
# * <tt>errors.attributes.title.blank</tt>
-
# * <tt>errors.messages.blank</tt>
-
#
-
1
def generate_message(attribute, type = :invalid, options = {})
-
type = options.delete(:message) if options[:message].is_a?(Symbol)
-
-
defaults = @base.class.lookup_ancestors.map do |klass|
-
[ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
-
:"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ]
-
end
-
-
defaults << options.delete(:message)
-
defaults << :"#{@base.class.i18n_scope}.errors.messages.#{type}"
-
defaults << :"errors.attributes.#{attribute}.#{type}"
-
defaults << :"errors.messages.#{type}"
-
-
defaults.compact!
-
defaults.flatten!
-
-
key = defaults.shift
-
value = (attribute != :base ? @base.send(:read_attribute_for_validation, attribute) : nil)
-
-
options = {
-
:default => defaults,
-
:model => @base.class.model_name.human,
-
:attribute => @base.class.human_attribute_name(attribute),
-
:value => value
-
}.merge(options)
-
-
I18n.translate(key, options)
-
end
-
end
-
end
-
1
require 'active_support/core_ext/class/attribute.rb'
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_model/mass_assignment_security/permission_set'
-
-
1
module ActiveModel
-
# = Active Model Mass-Assignment Security
-
1
module MassAssignmentSecurity
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
class_attribute :_accessible_attributes
-
1
class_attribute :_protected_attributes
-
1
class_attribute :_active_authorizer
-
end
-
-
# Mass assignment security provides an interface for protecting attributes
-
# from end-user assignment. For more complex permissions, mass assignment security
-
# may be handled outside the model by extending a non-ActiveRecord class,
-
# such as a controller, with this behavior.
-
#
-
# For example, a logged in user may need to assign additional attributes depending
-
# on their role:
-
#
-
# class AccountsController < ApplicationController
-
# include ActiveModel::MassAssignmentSecurity
-
#
-
# attr_accessible :first_name, :last_name
-
# attr_accessible :first_name, :last_name, :plan_id, :as => :admin
-
#
-
# def update
-
# ...
-
# @account.update_attributes(account_params)
-
# ...
-
# end
-
#
-
# protected
-
#
-
# def account_params
-
# role = admin ? :admin : :default
-
# sanitize_for_mass_assignment(params[:account], role)
-
# end
-
#
-
# end
-
#
-
1
module ClassMethods
-
# Attributes named in this macro are protected from mass-assignment
-
# whenever attributes are sanitized before assignment. A role for the
-
# attributes is optional, if no role is provided then :default is used.
-
# A role can be defined by using the :as option.
-
#
-
# Mass-assignment to these attributes will simply be ignored, to assign
-
# to them you can use direct writer methods. This is meant to protect
-
# sensitive attributes from being overwritten by malicious users
-
# tampering with URLs or forms. Example:
-
#
-
# class Customer
-
# include ActiveModel::MassAssignmentSecurity
-
#
-
# attr_accessor :name, :credit_rating
-
#
-
# attr_protected :credit_rating, :last_login
-
# attr_protected :last_login, :as => :admin
-
#
-
# def assign_attributes(values, options = {})
-
# sanitize_for_mass_assignment(values, options[:as]).each do |k, v|
-
# send("#{k}=", v)
-
# end
-
# end
-
# end
-
#
-
# When using the :default role :
-
#
-
# customer = Customer.new
-
# customer.assign_attributes({ "name" => "David", "credit_rating" => "Excellent", :last_login => 1.day.ago }, :as => :default)
-
# customer.name # => "David"
-
# customer.credit_rating # => nil
-
# customer.last_login # => nil
-
#
-
# customer.credit_rating = "Average"
-
# customer.credit_rating # => "Average"
-
#
-
# And using the :admin role :
-
#
-
# customer = Customer.new
-
# customer.assign_attributes({ "name" => "David", "credit_rating" => "Excellent", :last_login => 1.day.ago }, :as => :admin)
-
# customer.name # => "David"
-
# customer.credit_rating # => "Excellent"
-
# customer.last_login # => nil
-
#
-
# To start from an all-closed default and enable attributes as needed,
-
# have a look at +attr_accessible+.
-
#
-
# Note that using <tt>Hash#except</tt> or <tt>Hash#slice</tt> in place of +attr_protected+
-
# to sanitize attributes won't provide sufficient protection.
-
1
def attr_protected(*args)
-
options = args.extract_options!
-
role = options[:as] || :default
-
-
self._protected_attributes = protected_attributes_configs.dup
-
-
Array.wrap(role).each do |name|
-
self._protected_attributes[name] = self.protected_attributes(name) + args
-
end
-
-
self._active_authorizer = self._protected_attributes
-
end
-
-
# Specifies a white list of model attributes that can be set via
-
# mass-assignment.
-
#
-
# Like +attr_protected+, a role for the attributes is optional,
-
# if no role is provided then :default is used. A role can be defined by
-
# using the :as option.
-
#
-
# This is the opposite of the +attr_protected+ macro: Mass-assignment
-
# will only set attributes in this list, to assign to the rest of
-
# attributes you can use direct writer methods. This is meant to protect
-
# sensitive attributes from being overwritten by malicious users
-
# tampering with URLs or forms. If you'd rather start from an all-open
-
# default and restrict attributes as needed, have a look at
-
# +attr_protected+.
-
#
-
# class Customer
-
# include ActiveModel::MassAssignmentSecurity
-
#
-
# attr_accessor :name, :credit_rating
-
#
-
# attr_accessible :name
-
# attr_accessible :name, :credit_rating, :as => :admin
-
#
-
# def assign_attributes(values, options = {})
-
# sanitize_for_mass_assignment(values, options[:as]).each do |k, v|
-
# send("#{k}=", v)
-
# end
-
# end
-
# end
-
#
-
# When using the :default role :
-
#
-
# customer = Customer.new
-
# customer.assign_attributes({ "name" => "David", "credit_rating" => "Excellent", :last_login => 1.day.ago }, :as => :default)
-
# customer.name # => "David"
-
# customer.credit_rating # => nil
-
#
-
# customer.credit_rating = "Average"
-
# customer.credit_rating # => "Average"
-
#
-
# And using the :admin role :
-
#
-
# customer = Customer.new
-
# customer.assign_attributes({ "name" => "David", "credit_rating" => "Excellent", :last_login => 1.day.ago }, :as => :admin)
-
# customer.name # => "David"
-
# customer.credit_rating # => "Excellent"
-
#
-
# Note that using <tt>Hash#except</tt> or <tt>Hash#slice</tt> in place of +attr_accessible+
-
# to sanitize attributes won't provide sufficient protection.
-
1
def attr_accessible(*args)
-
options = args.extract_options!
-
role = options[:as] || :default
-
-
self._accessible_attributes = accessible_attributes_configs.dup
-
-
Array.wrap(role).each do |name|
-
self._accessible_attributes[name] = self.accessible_attributes(name) + args
-
end
-
-
self._active_authorizer = self._accessible_attributes
-
end
-
-
1
def protected_attributes(role = :default)
-
protected_attributes_configs[role]
-
end
-
-
1
def accessible_attributes(role = :default)
-
accessible_attributes_configs[role]
-
end
-
-
1
def active_authorizers
-
3
self._active_authorizer ||= protected_attributes_configs
-
end
-
1
alias active_authorizer active_authorizers
-
-
1
def attributes_protected_by_default
-
[]
-
end
-
-
1
private
-
-
1
def protected_attributes_configs
-
1
self._protected_attributes ||= begin
-
1
default_black_list = BlackList.new(attributes_protected_by_default).tap do |w|
-
1
w.logger = self.logger if self.respond_to?(:logger)
-
end
-
1
Hash.new(default_black_list)
-
end
-
end
-
-
1
def accessible_attributes_configs
-
self._accessible_attributes ||= begin
-
default_white_list = WhiteList.new.tap { |w| w.logger = self.logger if self.respond_to?(:logger) }
-
Hash.new(default_white_list)
-
end
-
end
-
end
-
-
1
protected
-
-
1
def sanitize_for_mass_assignment(attributes, role = :default)
-
3
mass_assignment_authorizer(role).sanitize(attributes)
-
end
-
-
1
def mass_assignment_authorizer(role = :default)
-
3
self.class.active_authorizer[role]
-
end
-
end
-
end
-
1
require 'set'
-
1
require 'active_model/mass_assignment_security/sanitizer'
-
-
1
module ActiveModel
-
1
module MassAssignmentSecurity
-
1
class PermissionSet < Set
-
1
attr_accessor :logger
-
-
1
def +(values)
-
super(values.map(&:to_s))
-
end
-
-
1
def include?(key)
-
12
super(remove_multiparameter_id(key))
-
end
-
-
1
protected
-
-
1
def remove_multiparameter_id(key)
-
12
key.to_s.gsub(/\(.+/, '')
-
end
-
end
-
-
1
class WhiteList < PermissionSet
-
1
include Sanitizer
-
-
1
def deny?(key)
-
!include?(key)
-
end
-
end
-
-
1
class BlackList < PermissionSet
-
1
include Sanitizer
-
-
1
def deny?(key)
-
12
include?(key)
-
end
-
end
-
end
-
end
-
1
module ActiveModel
-
1
module MassAssignmentSecurity
-
1
module Sanitizer
-
# Returns all attributes not denied by the authorizer.
-
1
def sanitize(attributes)
-
15
sanitized_attributes = attributes.reject { |key, value| deny?(key) }
-
3
debug_protected_attribute_removal(attributes, sanitized_attributes)
-
3
sanitized_attributes
-
end
-
-
1
protected
-
-
1
def debug_protected_attribute_removal(attributes, sanitized_attributes)
-
3
removed_keys = attributes.keys - sanitized_attributes.keys
-
3
warn!(removed_keys) if removed_keys.any?
-
end
-
-
1
def warn!(attrs)
-
3
self.logger.debug "WARNING: Can't mass-assign protected attributes: #{attrs.join(', ')}" if self.logger
-
end
-
end
-
end
-
end
-
1
require 'active_support/inflector'
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/module/introspection'
-
-
1
module ActiveModel
-
1
class Name < String
-
1
attr_reader :singular, :plural, :element, :collection, :partial_path, :route_key, :param_key, :i18n_key
-
1
alias_method :cache_key, :collection
-
-
1
def initialize(klass, namespace = nil, name = nil)
-
name ||= klass.name
-
super(name)
-
@unnamespaced = self.sub(/^#{namespace.name}::/, '') if namespace
-
-
@klass = klass
-
@singular = _singularize(self).freeze
-
@plural = ActiveSupport::Inflector.pluralize(@singular).freeze
-
@element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self)).freeze
-
@human = ActiveSupport::Inflector.humanize(@element).freeze
-
@collection = ActiveSupport::Inflector.tableize(self).freeze
-
@partial_path = "#{@collection}/#{@element}".freeze
-
@param_key = (namespace ? _singularize(@unnamespaced) : @singular).freeze
-
@route_key = (namespace ? ActiveSupport::Inflector.pluralize(@param_key) : @plural).freeze
-
@i18n_key = self.underscore.to_sym
-
end
-
-
# Transform the model name into a more humane format, using I18n. By default,
-
# it will underscore then humanize the class name
-
#
-
# BlogPost.model_name.human # => "Blog post"
-
#
-
# Specify +options+ with additional translating options.
-
1
def human(options={})
-
return @human unless @klass.respond_to?(:lookup_ancestors) &&
-
@klass.respond_to?(:i18n_scope)
-
-
defaults = @klass.lookup_ancestors.map do |klass|
-
klass.model_name.i18n_key
-
end
-
-
defaults << options[:default] if options[:default]
-
defaults << @human
-
-
options = {:scope => [@klass.i18n_scope, :models], :count => 1, :default => defaults}.merge(options.except(:default))
-
I18n.translate(defaults.shift, options)
-
end
-
-
1
private
-
-
1
def _singularize(string, replacement='_')
-
ActiveSupport::Inflector.underscore(string).tr('/', replacement)
-
end
-
end
-
-
# == Active Model Naming
-
#
-
# Creates a +model_name+ method on your object.
-
#
-
# To implement, just extend ActiveModel::Naming in your object:
-
#
-
# class BookCover
-
# extend ActiveModel::Naming
-
# end
-
#
-
# BookCover.model_name # => "BookCover"
-
# BookCover.model_name.human # => "Book cover"
-
#
-
# BookCover.model_name.i18n_key # => "book_cover"
-
# BookModule::BookCover.model_name.i18n_key # => "book_module.book_cover"
-
#
-
# Providing the functionality that ActiveModel::Naming provides in your object
-
# is required to pass the Active Model Lint test. So either extending the provided
-
# method below, or rolling your own is required.
-
1
module Naming
-
# Returns an ActiveModel::Name object for module. It can be
-
# used to retrieve all kinds of naming-related information.
-
1
def model_name
-
@_model_name ||= begin
-
namespace = self.parents.detect { |n| n.respond_to?(:_railtie) }
-
ActiveModel::Name.new(self, namespace)
-
end
-
end
-
-
# Returns the plural class name of a record or class. Examples:
-
#
-
# ActiveModel::Naming.plural(post) # => "posts"
-
# ActiveModel::Naming.plural(Highrise::Person) # => "highrise_people"
-
1
def self.plural(record_or_class)
-
model_name_from_record_or_class(record_or_class).plural
-
end
-
-
# Returns the singular class name of a record or class. Examples:
-
#
-
# ActiveModel::Naming.singular(post) # => "post"
-
# ActiveModel::Naming.singular(Highrise::Person) # => "highrise_person"
-
1
def self.singular(record_or_class)
-
model_name_from_record_or_class(record_or_class).singular
-
end
-
-
# Identifies whether the class name of a record or class is uncountable. Examples:
-
#
-
# ActiveModel::Naming.uncountable?(Sheep) # => true
-
# ActiveModel::Naming.uncountable?(Post) => false
-
1
def self.uncountable?(record_or_class)
-
plural(record_or_class) == singular(record_or_class)
-
end
-
-
# Returns string to use while generating route names. It differs for
-
# namespaced models regarding whether it's inside isolated engine.
-
#
-
# For isolated engine:
-
# ActiveModel::Naming.route_key(Blog::Post) #=> posts
-
#
-
# For shared engine:
-
# ActiveModel::Naming.route_key(Blog::Post) #=> blog_posts
-
1
def self.route_key(record_or_class)
-
model_name_from_record_or_class(record_or_class).route_key
-
end
-
-
# Returns string to use for params names. It differs for
-
# namespaced models regarding whether it's inside isolated engine.
-
#
-
# For isolated engine:
-
# ActiveModel::Naming.param_key(Blog::Post) #=> post
-
#
-
# For shared engine:
-
# ActiveModel::Naming.param_key(Blog::Post) #=> blog_post
-
1
def self.param_key(record_or_class)
-
model_name_from_record_or_class(record_or_class).param_key
-
end
-
-
1
private
-
1
def self.model_name_from_record_or_class(record_or_class)
-
(record_or_class.is_a?(Class) ? record_or_class : convert_to_model(record_or_class).class).model_name
-
end
-
-
1
def self.convert_to_model(object)
-
object.respond_to?(:to_model) ? object.to_model : object
-
end
-
end
-
-
end
-
1
require 'set'
-
-
1
module ActiveModel
-
# Stores the enabled/disabled state of individual observers for
-
# a particular model class.
-
1
class ObserverArray < Array
-
1
attr_reader :model_class
-
1
def initialize(model_class, *args)
-
1
@model_class = model_class
-
1
super(*args)
-
end
-
-
# Returns true if the given observer is disabled for the model class.
-
1
def disabled_for?(observer)
-
disabled_observers.include?(observer.class)
-
end
-
-
# Disables one or more observers. This supports multiple forms:
-
#
-
# ORM.observers.disable :user_observer
-
# # => disables the UserObserver
-
#
-
# User.observers.disable AuditTrail
-
# # => disables the AuditTrail observer for User notifications.
-
# # Other models will still notify the AuditTrail observer.
-
#
-
# ORM.observers.disable :observer_1, :observer_2
-
# # => disables Observer1 and Observer2 for all models.
-
#
-
# ORM.observers.disable :all
-
# # => disables all observers for all models.
-
#
-
# User.observers.disable :all do
-
# # all user observers are disabled for
-
# # just the duration of the block
-
# end
-
1
def disable(*observers, &block)
-
set_enablement(false, observers, &block)
-
end
-
-
# Enables one or more observers. This supports multiple forms:
-
#
-
# ORM.observers.enable :user_observer
-
# # => enables the UserObserver
-
#
-
# User.observers.enable AuditTrail
-
# # => enables the AuditTrail observer for User notifications.
-
# # Other models will not be affected (i.e. they will not
-
# # trigger notifications to AuditTrail if previously disabled)
-
#
-
# ORM.observers.enable :observer_1, :observer_2
-
# # => enables Observer1 and Observer2 for all models.
-
#
-
# ORM.observers.enable :all
-
# # => enables all observers for all models.
-
#
-
# User.observers.enable :all do
-
# # all user observers are enabled for
-
# # just the duration of the block
-
# end
-
#
-
# Note: all observers are enabled by default. This method is only
-
# useful when you have previously disabled one or more observers.
-
1
def enable(*observers, &block)
-
set_enablement(true, observers, &block)
-
end
-
-
1
protected
-
-
1
def disabled_observers
-
@disabled_observers ||= Set.new
-
end
-
-
1
def observer_class_for(observer)
-
return observer if observer.is_a?(Class)
-
-
if observer.respond_to?(:to_sym) # string/symbol
-
observer.to_s.camelize.constantize
-
else
-
raise ArgumentError, "#{observer} was not a class or a " +
-
"lowercase, underscored class name as expected."
-
end
-
end
-
-
1
def start_transaction
-
disabled_observer_stack.push(disabled_observers.dup)
-
each_subclass_array do |array|
-
array.start_transaction
-
end
-
end
-
-
1
def disabled_observer_stack
-
@disabled_observer_stack ||= []
-
end
-
-
1
def end_transaction
-
@disabled_observers = disabled_observer_stack.pop
-
each_subclass_array do |array|
-
array.end_transaction
-
end
-
end
-
-
1
def transaction
-
start_transaction
-
-
begin
-
yield
-
ensure
-
end_transaction
-
end
-
end
-
-
1
def each_subclass_array
-
model_class.descendants.each do |subclass|
-
yield subclass.observers
-
end
-
end
-
-
1
def set_enablement(enabled, observers)
-
if block_given?
-
transaction do
-
set_enablement(enabled, observers)
-
yield
-
end
-
else
-
observers = ActiveModel::Observer.descendants if observers == [:all]
-
observers.each do |obs|
-
klass = observer_class_for(obs)
-
-
unless klass < ActiveModel::Observer
-
raise ArgumentError.new("#{obs} does not refer to a valid observer")
-
end
-
-
if enabled
-
disabled_observers.delete(klass)
-
else
-
disabled_observers << klass
-
end
-
end
-
-
each_subclass_array do |array|
-
array.set_enablement(enabled, observers)
-
end
-
end
-
end
-
end
-
end
-
1
require 'singleton'
-
1
require 'active_model/observer_array'
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_support/core_ext/module/aliasing'
-
1
require 'active_support/core_ext/module/remove_method'
-
1
require 'active_support/core_ext/string/inflections'
-
1
require 'active_support/core_ext/enumerable'
-
1
require 'active_support/descendants_tracker'
-
-
1
module ActiveModel
-
1
module Observing
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
extend ActiveSupport::DescendantsTracker
-
end
-
-
1
module ClassMethods
-
# == Active Model Observers Activation
-
#
-
# Activates the observers assigned. Examples:
-
#
-
# class ORM
-
# include ActiveModel::Observing
-
# end
-
#
-
# # Calls PersonObserver.instance
-
# ORM.observers = :person_observer
-
#
-
# # Calls Cacher.instance and GarbageCollector.instance
-
# ORM.observers = :cacher, :garbage_collector
-
#
-
# # Same as above, just using explicit class references
-
# ORM.observers = Cacher, GarbageCollector
-
#
-
# Note: Setting this does not instantiate the observers yet.
-
# +instantiate_observers+ is called during startup, and before
-
# each development request.
-
1
def observers=(*values)
-
observers.replace(values.flatten)
-
end
-
-
# Gets an array of observers observing this model.
-
# The array also provides +enable+ and +disable+ methods
-
# that allow you to selectively enable and disable observers.
-
# (see <tt>ActiveModel::ObserverArray.enable</tt> and
-
# <tt>ActiveModel::ObserverArray.disable</tt> for more on this)
-
1
def observers
-
1
@observers ||= ObserverArray.new(self)
-
end
-
-
# Gets the current observer instances.
-
1
def observer_instances
-
9
@observer_instances ||= []
-
end
-
-
# Instantiate the global observers.
-
1
def instantiate_observers
-
1
observers.each { |o| instantiate_observer(o) }
-
end
-
-
# Add a new observer to the pool.
-
# The new observer needs to respond to 'update', otherwise it
-
# raises an +ArgumentError+ exception.
-
1
def add_observer(observer)
-
unless observer.respond_to? :update
-
raise ArgumentError, "observer needs to respond to `update'"
-
end
-
observer_instances << observer
-
end
-
-
# Notify list of observers of a change.
-
1
def notify_observers(*arg)
-
9
observer_instances.each { |observer| observer.update(*arg) }
-
end
-
-
# Total number of observers.
-
1
def count_observers
-
observer_instances.size
-
end
-
-
1
protected
-
1
def instantiate_observer(observer) #:nodoc:
-
# string/symbol
-
if observer.respond_to?(:to_sym)
-
observer.to_s.camelize.constantize.instance
-
elsif observer.respond_to?(:instance)
-
observer.instance
-
else
-
raise ArgumentError,
-
"#{observer} must be a lowercase, underscored class name (or an " +
-
"instance of the class itself) responding to the instance " +
-
"method. Example: Person.observers = :big_brother # calls " +
-
"BigBrother.instance"
-
end
-
end
-
-
# Notify observers when the observed class is subclassed.
-
1
def inherited(subclass)
-
9
super
-
9
notify_observers :observed_class_inherited, subclass
-
end
-
end
-
-
1
private
-
# Fires notifications to model's observers
-
#
-
# def save
-
# notify_observers(:before_save)
-
# ...
-
# notify_observers(:after_save)
-
# end
-
1
def notify_observers(method)
-
self.class.notify_observers(method, self)
-
end
-
end
-
-
# == Active Model Observers
-
#
-
# Observer classes respond to life cycle callbacks to implement trigger-like
-
# behavior outside the original class. This is a great way to reduce the
-
# clutter that normally comes when the model class is burdened with
-
# functionality that doesn't pertain to the core responsibility of the
-
# class. Example:
-
#
-
# class CommentObserver < ActiveModel::Observer
-
# def after_save(comment)
-
# Notifications.comment("admin@do.com", "New comment was posted", comment).deliver
-
# end
-
# end
-
#
-
# This Observer sends an email when a Comment#save is finished.
-
#
-
# class ContactObserver < ActiveModel::Observer
-
# def after_create(contact)
-
# contact.logger.info('New contact added!')
-
# end
-
#
-
# def after_destroy(contact)
-
# contact.logger.warn("Contact with an id of #{contact.id} was destroyed!")
-
# end
-
# end
-
#
-
# This Observer uses logger to log when specific callbacks are triggered.
-
#
-
# == Observing a class that can't be inferred
-
#
-
# Observers will by default be mapped to the class with which they share a
-
# name. So CommentObserver will be tied to observing Comment, ProductManagerObserver
-
# to ProductManager, and so on. If you want to name your observer differently than
-
# the class you're interested in observing, you can use the <tt>Observer.observe</tt>
-
# class method which takes either the concrete class (Product) or a symbol for that
-
# class (:product):
-
#
-
# class AuditObserver < ActiveModel::Observer
-
# observe :account
-
#
-
# def after_update(account)
-
# AuditTrail.new(account, "UPDATED")
-
# end
-
# end
-
#
-
# If the audit observer needs to watch more than one kind of object, this can be
-
# specified with multiple arguments:
-
#
-
# class AuditObserver < ActiveModel::Observer
-
# observe :account, :balance
-
#
-
# def after_update(record)
-
# AuditTrail.new(record, "UPDATED")
-
# end
-
# end
-
#
-
# The AuditObserver will now act on both updates to Account and Balance by treating
-
# them both as records.
-
#
-
# If you're using an Observer in a Rails application with Active Record, be sure to
-
# read about the necessary configuration in the documentation for
-
# ActiveRecord::Observer.
-
#
-
1
class Observer
-
1
include Singleton
-
1
extend ActiveSupport::DescendantsTracker
-
-
1
class << self
-
# Attaches the observer to the supplied model classes.
-
1
def observe(*models)
-
models.flatten!
-
models.collect! { |model| model.respond_to?(:to_sym) ? model.to_s.camelize.constantize : model }
-
remove_possible_method(:observed_classes)
-
define_method(:observed_classes) { models }
-
end
-
-
# Returns an array of Classes to observe.
-
#
-
# You can override this instead of using the +observe+ helper.
-
#
-
# class AuditObserver < ActiveModel::Observer
-
# def self.observed_classes
-
# [Account, Balance]
-
# end
-
# end
-
1
def observed_classes
-
Array.wrap(observed_class)
-
end
-
-
# The class observed by default is inferred from the observer's class name:
-
# assert_equal Person, PersonObserver.observed_class
-
1
def observed_class
-
if observed_class_name = name[/(.*)Observer/, 1]
-
observed_class_name.constantize
-
else
-
nil
-
end
-
end
-
end
-
-
# Start observing the declared classes and their subclasses.
-
1
def initialize
-
observed_classes.each { |klass| add_observer!(klass) }
-
end
-
-
1
def observed_classes #:nodoc:
-
self.class.observed_classes
-
end
-
-
# Send observed_method(object) if the method exists and
-
# the observer is enabled for the given object's class.
-
1
def update(observed_method, object, &block) #:nodoc:
-
return unless respond_to?(observed_method)
-
return if disabled_for?(object)
-
send(observed_method, object, &block)
-
end
-
-
# Special method sent by the observed class when it is inherited.
-
# Passes the new subclass.
-
1
def observed_class_inherited(subclass) #:nodoc:
-
self.class.observe(observed_classes + [subclass])
-
add_observer!(subclass)
-
end
-
-
1
protected
-
1
def add_observer!(klass) #:nodoc:
-
klass.add_observer(self)
-
end
-
-
1
def disabled_for?(object)
-
klass = object.class
-
return false unless klass.respond_to?(:observers)
-
klass.observers.disabled_for?(self)
-
end
-
end
-
end
-
1
require "active_model"
-
1
require "rails"
-
1
require 'bcrypt'
-
-
1
module ActiveModel
-
1
module SecurePassword
-
1
extend ActiveSupport::Concern
-
-
1
module ClassMethods
-
# Adds methods to set and authenticate against a BCrypt password.
-
# This mechanism requires you to have a password_digest attribute.
-
#
-
# Validations for presence of password, confirmation of password (using
-
# a "password_confirmation" attribute) are automatically added.
-
# You can add more validations by hand if need be.
-
#
-
# Example using Active Record (which automatically includes ActiveModel::SecurePassword):
-
#
-
# # Schema: User(name:string, password_digest:string)
-
# class User < ActiveRecord::Base
-
# has_secure_password
-
# end
-
#
-
# user = User.new(:name => "david", :password => "", :password_confirmation => "nomatch")
-
# user.save # => false, password required
-
# user.password = "mUc3m00RsqyRe"
-
# user.save # => false, confirmation doesn't match
-
# user.password_confirmation = "mUc3m00RsqyRe"
-
# user.save # => true
-
# user.authenticate("notright") # => false
-
# user.authenticate("mUc3m00RsqyRe") # => user
-
# User.find_by_name("david").try(:authenticate, "notright") # => nil
-
# User.find_by_name("david").try(:authenticate, "mUc3m00RsqyRe") # => user
-
1
def has_secure_password
-
attr_reader :password
-
-
validates_confirmation_of :password
-
validates_presence_of :password_digest
-
-
include InstanceMethodsOnActivation
-
-
if respond_to?(:attributes_protected_by_default)
-
def self.attributes_protected_by_default
-
super + ['password_digest']
-
end
-
end
-
end
-
end
-
-
1
module InstanceMethodsOnActivation
-
# Returns self if the password is correct, otherwise false.
-
1
def authenticate(unencrypted_password)
-
if BCrypt::Password.new(password_digest) == unencrypted_password
-
self
-
else
-
false
-
end
-
end
-
-
# Encrypts the password into the password_digest attribute.
-
1
def password=(unencrypted_password)
-
@password = unencrypted_password
-
unless unencrypted_password.blank?
-
self.password_digest = BCrypt::Password.create(unencrypted_password)
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/hash/slice'
-
1
require 'active_support/core_ext/array/wrap'
-
-
-
1
module ActiveModel
-
# == Active Model Serialization
-
#
-
# Provides a basic serialization to a serializable_hash for your object.
-
#
-
# A minimal implementation could be:
-
#
-
# class Person
-
#
-
# include ActiveModel::Serialization
-
#
-
# attr_accessor :name
-
#
-
# def attributes
-
# {'name' => name}
-
# end
-
#
-
# end
-
#
-
# Which would provide you with:
-
#
-
# person = Person.new
-
# person.serializable_hash # => {"name"=>nil}
-
# person.name = "Bob"
-
# person.serializable_hash # => {"name"=>"Bob"}
-
#
-
# You need to declare some sort of attributes hash which contains the attributes
-
# you want to serialize and their current value.
-
#
-
# Most of the time though, you will want to include the JSON or XML
-
# serializations. Both of these modules automatically include the
-
# ActiveModel::Serialization module, so there is no need to explicitly
-
# include it.
-
#
-
# So a minimal implementation including XML and JSON would be:
-
#
-
# class Person
-
#
-
# include ActiveModel::Serializers::JSON
-
# include ActiveModel::Serializers::Xml
-
#
-
# attr_accessor :name
-
#
-
# def attributes
-
# {'name' => name}
-
# end
-
#
-
# end
-
#
-
# Which would provide you with:
-
#
-
# person = Person.new
-
# person.serializable_hash # => {"name"=>nil}
-
# person.as_json # => {"name"=>nil}
-
# person.to_json # => "{\"name\":null}"
-
# person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person...
-
#
-
# person.name = "Bob"
-
# person.serializable_hash # => {"name"=>"Bob"}
-
# person.as_json # => {"name"=>"Bob"}
-
# person.to_json # => "{\"name\":\"Bob\"}"
-
# person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person...
-
#
-
# Valid options are <tt>:only</tt>, <tt>:except</tt> and <tt>:methods</tt> .
-
1
module Serialization
-
1
def serializable_hash(options = nil)
-
options ||= {}
-
-
only = Array.wrap(options[:only]).map(&:to_s)
-
except = Array.wrap(options[:except]).map(&:to_s)
-
-
attribute_names = attributes.keys.sort
-
if only.any?
-
attribute_names &= only
-
elsif except.any?
-
attribute_names -= except
-
end
-
-
method_names = Array.wrap(options[:methods]).map { |n| n if respond_to?(n.to_s) }.compact
-
Hash[(attribute_names + method_names).map { |n| [n, send(n)] }]
-
end
-
end
-
end
-
1
require 'active_support/json'
-
1
require 'active_support/core_ext/class/attribute'
-
-
1
module ActiveModel
-
# == Active Model JSON Serializer
-
1
module Serializers
-
1
module JSON
-
1
extend ActiveSupport::Concern
-
1
include ActiveModel::Serialization
-
-
1
included do
-
1
extend ActiveModel::Naming
-
-
1
class_attribute :include_root_in_json
-
1
self.include_root_in_json = true
-
end
-
-
# Returns a JSON string representing the model. Some configuration can be
-
# passed through +options+.
-
#
-
# The option <tt>include_root_in_json</tt> controls the top-level behavior
-
# of +as_json+. If true (the default) +as_json+ will emit a single root
-
# node named after the object's type. For example:
-
#
-
# user = User.find(1)
-
# user.as_json
-
# # => { "user": {"id": 1, "name": "Konata Izumi", "age": 16,
-
# "created_at": "2006/08/01", "awesome": true} }
-
#
-
# ActiveRecord::Base.include_root_in_json = false
-
# user.as_json
-
# # => {"id": 1, "name": "Konata Izumi", "age": 16,
-
# "created_at": "2006/08/01", "awesome": true}
-
#
-
# The remainder of the examples in this section assume +include_root_in_json+
-
# is false.
-
#
-
# Without any +options+, the returned JSON string will include all the model's
-
# attributes. For example:
-
#
-
# user = User.find(1)
-
# user.as_json
-
# # => {"id": 1, "name": "Konata Izumi", "age": 16,
-
# "created_at": "2006/08/01", "awesome": true}
-
#
-
# The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the attributes
-
# included, and work similar to the +attributes+ method. For example:
-
#
-
# user.as_json(:only => [ :id, :name ])
-
# # => {"id": 1, "name": "Konata Izumi"}
-
#
-
# user.as_json(:except => [ :id, :created_at, :age ])
-
# # => {"name": "Konata Izumi", "awesome": true}
-
#
-
# To include the result of some method calls on the model use <tt>:methods</tt>:
-
#
-
# user.as_json(:methods => :permalink)
-
# # => {"id": 1, "name": "Konata Izumi", "age": 16,
-
# "created_at": "2006/08/01", "awesome": true,
-
# "permalink": "1-konata-izumi"}
-
#
-
# To include associations use <tt>:include</tt>:
-
#
-
# user.as_json(:include => :posts)
-
# # => {"id": 1, "name": "Konata Izumi", "age": 16,
-
# "created_at": "2006/08/01", "awesome": true,
-
# "posts": [{"id": 1, "author_id": 1, "title": "Welcome to the weblog"},
-
# {"id": 2, author_id: 1, "title": "So I was thinking"}]}
-
#
-
# Second level and higher order associations work as well:
-
#
-
# user.as_json(:include => { :posts => {
-
# :include => { :comments => {
-
# :only => :body } },
-
# :only => :title } })
-
# # => {"id": 1, "name": "Konata Izumi", "age": 16,
-
# "created_at": "2006/08/01", "awesome": true,
-
# "posts": [{"comments": [{"body": "1st post!"}, {"body": "Second!"}],
-
# "title": "Welcome to the weblog"},
-
# {"comments": [{"body": "Don't think too hard"}],
-
# "title": "So I was thinking"}]}
-
-
1
def as_json(options = nil)
-
hash = serializable_hash(options)
-
-
if include_root_in_json
-
custom_root = options && options[:root]
-
hash = { custom_root || self.class.model_name.element => hash }
-
end
-
-
hash
-
end
-
-
1
def from_json(json)
-
hash = ActiveSupport::JSON.decode(json)
-
hash = hash.values.first if include_root_in_json
-
self.attributes = hash
-
self
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_support/core_ext/class/attribute_accessors'
-
1
require 'active_support/core_ext/array/conversions'
-
1
require 'active_support/core_ext/hash/conversions'
-
1
require 'active_support/core_ext/hash/slice'
-
-
1
module ActiveModel
-
# == Active Model XML Serializer
-
1
module Serializers
-
1
module Xml
-
1
extend ActiveSupport::Concern
-
1
include ActiveModel::Serialization
-
-
1
class Serializer #:nodoc:
-
1
class Attribute #:nodoc:
-
1
attr_reader :name, :value, :type
-
-
1
def initialize(name, serializable, raw_value=nil)
-
@name, @serializable = name, serializable
-
raw_value = raw_value.in_time_zone if raw_value.respond_to?(:in_time_zone)
-
@value = raw_value || @serializable.send(name)
-
@type = compute_type
-
end
-
-
1
def decorations
-
decorations = {}
-
decorations[:encoding] = 'base64' if type == :binary
-
decorations[:type] = (type == :string) ? nil : type
-
decorations[:nil] = true if value.nil?
-
decorations
-
end
-
-
1
protected
-
-
1
def compute_type
-
return if value.nil?
-
type = ActiveSupport::XmlMini::TYPE_NAMES[value.class.name]
-
type ||= :string if value.respond_to?(:to_str)
-
type ||= :yaml
-
type
-
end
-
end
-
-
1
class MethodAttribute < Attribute #:nodoc:
-
end
-
-
1
attr_reader :options
-
-
1
def initialize(serializable, options = nil)
-
@serializable = serializable
-
@options = options ? options.dup : {}
-
-
@options[:only] = Array.wrap(@options[:only]).map { |n| n.to_s }
-
@options[:except] = Array.wrap(@options[:except]).map { |n| n.to_s }
-
end
-
-
# To replicate the behavior in ActiveRecord#attributes, <tt>:except</tt>
-
# takes precedence over <tt>:only</tt>. If <tt>:only</tt> is not set
-
# for a N level model but is set for the N+1 level models,
-
# then because <tt>:except</tt> is set to a default value, the second
-
# level model can have both <tt>:except</tt> and <tt>:only</tt> set. So if
-
# <tt>:only</tt> is set, always delete <tt>:except</tt>.
-
1
def attributes_hash
-
attributes = @serializable.attributes
-
if options[:only].any?
-
attributes.slice(*options[:only])
-
elsif options[:except].any?
-
attributes.except(*options[:except])
-
else
-
attributes
-
end
-
end
-
-
1
def serializable_attributes
-
attributes_hash.map do |name, value|
-
self.class::Attribute.new(name, @serializable, value)
-
end
-
end
-
-
1
def serializable_methods
-
Array.wrap(options[:methods]).map do |name|
-
self.class::MethodAttribute.new(name.to_s, @serializable) if @serializable.respond_to?(name.to_s)
-
end.compact
-
end
-
-
1
def serialize
-
require 'builder' unless defined? ::Builder
-
-
options[:indent] ||= 2
-
options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
-
-
@builder = options[:builder]
-
@builder.instruct! unless options[:skip_instruct]
-
-
root = (options[:root] || @serializable.class.model_name.element).to_s
-
root = ActiveSupport::XmlMini.rename_key(root, options)
-
-
args = [root]
-
args << {:xmlns => options[:namespace]} if options[:namespace]
-
args << {:type => options[:type]} if options[:type] && !options[:skip_types]
-
-
@builder.tag!(*args) do
-
add_attributes_and_methods
-
add_extra_behavior
-
add_procs
-
yield @builder if block_given?
-
end
-
end
-
-
1
private
-
-
1
def add_extra_behavior
-
end
-
-
1
def add_attributes_and_methods
-
(serializable_attributes + serializable_methods).each do |attribute|
-
key = ActiveSupport::XmlMini.rename_key(attribute.name, options)
-
ActiveSupport::XmlMini.to_tag(key, attribute.value,
-
options.merge(attribute.decorations))
-
end
-
end
-
-
1
def add_procs
-
if procs = options.delete(:procs)
-
Array.wrap(procs).each do |proc|
-
if proc.arity == 1
-
proc.call(options)
-
else
-
proc.call(options, @serializable)
-
end
-
end
-
end
-
end
-
end
-
-
# Returns XML representing the model. Configuration can be
-
# passed through +options+.
-
#
-
# Without any +options+, the returned XML string will include all the model's
-
# attributes. For example:
-
#
-
# user = User.find(1)
-
# user.to_xml
-
#
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <user>
-
# <id type="integer">1</id>
-
# <name>David</name>
-
# <age type="integer">16</age>
-
# <created-at type="datetime">2011-01-30T22:29:23Z</created-at>
-
# </user>
-
#
-
# The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the attributes
-
# included, and work similar to the +attributes+ method.
-
#
-
# To include the result of some method calls on the model use <tt>:methods</tt>.
-
#
-
# To include associations use <tt>:include</tt>.
-
#
-
# For further documentation see activerecord/lib/active_record/serializers/xml_serializer.xml.
-
1
def to_xml(options = {}, &block)
-
Serializer.new(self, options).serialize(&block)
-
end
-
-
1
def from_xml(xml)
-
self.attributes = Hash.from_xml(xml).values.first
-
self
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/reverse_merge'
-
-
1
module ActiveModel
-
-
# == Active Model Translation
-
#
-
# Provides integration between your object and the Rails internationalization
-
# (i18n) framework.
-
#
-
# A minimal implementation could be:
-
#
-
# class TranslatedPerson
-
# extend ActiveModel::Translation
-
# end
-
#
-
# TranslatedPerson.human_attribute_name('my_attribute')
-
# # => "My attribute"
-
#
-
# This also provides the required class methods for hooking into the
-
# Rails internationalization API, including being able to define a
-
# class based +i18n_scope+ and +lookup_ancestors+ to find translations in
-
# parent classes.
-
1
module Translation
-
1
include ActiveModel::Naming
-
-
# Returns the +i18n_scope+ for the class. Overwrite if you want custom lookup.
-
1
def i18n_scope
-
:activemodel
-
end
-
-
# When localizing a string, it goes through the lookup returned by this
-
# method, which is used in ActiveModel::Name#human,
-
# ActiveModel::Errors#full_messages and
-
# ActiveModel::Translation#human_attribute_name.
-
1
def lookup_ancestors
-
self.ancestors.select { |x| x.respond_to?(:model_name) }
-
end
-
-
# Transforms attribute names into a more human format, such as "First name"
-
# instead of "first_name".
-
#
-
# Person.human_attribute_name("first_name") # => "First name"
-
#
-
# Specify +options+ with additional translating options.
-
1
def human_attribute_name(attribute, options = {})
-
defaults = lookup_ancestors.map do |klass|
-
:"#{self.i18n_scope}.attributes.#{klass.model_name.i18n_key}.#{attribute}"
-
end
-
-
defaults << :"attributes.#{attribute}"
-
defaults << options.delete(:default) if options[:default]
-
defaults << attribute.to_s.humanize
-
-
options.reverse_merge! :count => 1, :default => defaults
-
I18n.translate(defaults.shift, options)
-
end
-
end
-
end
-
1
require 'active_support/core_ext/array/extract_options'
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_model/errors'
-
1
require 'active_model/validations/callbacks'
-
-
1
module ActiveModel
-
-
# == Active Model Validations
-
#
-
# Provides a full validation framework to your objects.
-
#
-
# A minimal implementation could be:
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# attr_accessor :first_name, :last_name
-
#
-
# validates_each :first_name, :last_name do |record, attr, value|
-
# record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
-
# end
-
# end
-
#
-
# Which provides you with the full standard validation stack that you
-
# know from Active Record:
-
#
-
# person = Person.new
-
# person.valid? # => true
-
# person.invalid? # => false
-
#
-
# person.first_name = 'zoolander'
-
# person.valid? # => false
-
# person.invalid? # => true
-
# person.errors # => #<OrderedHash {:first_name=>["starts with z."]}>
-
#
-
# Note that <tt>ActiveModel::Validations</tt> automatically adds an +errors+ method
-
# to your instances initialized with a new <tt>ActiveModel::Errors</tt> object, so
-
# there is no need for you to do this manually.
-
#
-
1
module Validations
-
1
extend ActiveSupport::Concern
-
1
include ActiveSupport::Callbacks
-
-
1
included do
-
1
extend ActiveModel::Translation
-
-
1
extend HelperMethods
-
1
include HelperMethods
-
-
1
attr_accessor :validation_context
-
1
define_callbacks :validate, :scope => :name
-
-
1
class_attribute :_validators
-
10
self._validators = Hash.new { |h,k| h[k] = [] }
-
end
-
-
1
module ClassMethods
-
# Validates each attribute against a block.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# attr_accessor :first_name, :last_name
-
#
-
# validates_each :first_name, :last_name do |record, attr, value|
-
# record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
-
# end
-
# end
-
#
-
# Options:
-
# * <tt>:on</tt> - Specifies the context where this validation is active
-
# (e.g. <tt>:on => :create</tt> or <tt>:on => :custom_validation_context</tt>)
-
# * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+.
-
# * <tt>:allow_blank</tt> - Skip validation if attribute is blank.
-
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
-
# if the validation should occur (e.g. <tt>:if => :allow_validation</tt>,
-
# or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The method,
-
# proc or string should return or evaluate to a true or false value.
-
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
-
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or
-
# <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
-
# method, proc or string should return or evaluate to a true or false value.
-
1
def validates_each(*attr_names, &block)
-
options = attr_names.extract_options!.symbolize_keys
-
validates_with BlockValidator, options.merge(:attributes => attr_names.flatten), &block
-
end
-
-
# Adds a validation method or block to the class. This is useful when
-
# overriding the +validate+ instance method becomes too unwieldy and
-
# you're looking for more descriptive declaration of your validations.
-
#
-
# This can be done with a symbol pointing to a method:
-
#
-
# class Comment
-
# include ActiveModel::Validations
-
#
-
# validate :must_be_friends
-
#
-
# def must_be_friends
-
# errors.add(:base, "Must be friends to leave a comment") unless commenter.friend_of?(commentee)
-
# end
-
# end
-
#
-
# With a block which is passed with the current record to be validated:
-
#
-
# class Comment
-
# include ActiveModel::Validations
-
#
-
# validate do |comment|
-
# comment.must_be_friends
-
# end
-
#
-
# def must_be_friends
-
# errors.add(:base, "Must be friends to leave a comment") unless commenter.friend_of?(commentee)
-
# end
-
# end
-
#
-
# Or with a block where self points to the current record to be validated:
-
#
-
# class Comment
-
# include ActiveModel::Validations
-
#
-
# validate do
-
# errors.add(:base, "Must be friends to leave a comment") unless commenter.friend_of?(commentee)
-
# end
-
# end
-
#
-
1
def validate(*args, &block)
-
11
options = args.extract_options!
-
11
if options.key?(:on)
-
options = options.dup
-
options[:if] = Array.wrap(options[:if])
-
options[:if].unshift("validation_context == :#{options[:on]}")
-
end
-
11
args << options
-
11
set_callback(:validate, *args, &block)
-
end
-
-
# List all validators that are being used to validate the model using
-
# +validates_with+ method.
-
1
def validators
-
_validators.values.flatten.uniq
-
end
-
-
# List all validators that being used to validate a specific attribute.
-
1
def validators_on(*attributes)
-
attributes.map do |attribute|
-
_validators[attribute.to_sym]
-
end.flatten
-
end
-
-
# Check if method is an attribute method or not.
-
1
def attribute_method?(attribute)
-
method_defined?(attribute)
-
end
-
-
# Copy validators on inheritance.
-
1
def inherited(base)
-
9
dup = _validators.dup
-
33
base._validators = dup.each { |k, v| dup[k] = v.dup }
-
9
super
-
end
-
end
-
-
# Returns the +Errors+ object that holds all information about attribute error messages.
-
1
def errors
-
@errors ||= Errors.new(self)
-
end
-
-
# Runs all the specified validations and returns true if no errors were added
-
# otherwise false. Context can optionally be supplied to define which callbacks
-
# to test against (the context is defined on the validations using :on).
-
1
def valid?(context = nil)
-
current_context, self.validation_context = validation_context, context
-
errors.clear
-
run_validations!
-
ensure
-
self.validation_context = current_context
-
end
-
-
# Performs the opposite of <tt>valid?</tt>. Returns true if errors were added,
-
# false otherwise.
-
1
def invalid?(context = nil)
-
!valid?(context)
-
end
-
-
# Hook method defining how an attribute value should be retrieved. By default
-
# this is assumed to be an instance named after the attribute. Override this
-
# method in subclasses should you need to retrieve the value for a given
-
# attribute differently:
-
#
-
# class MyClass
-
# include ActiveModel::Validations
-
#
-
# def initialize(data = {})
-
# @data = data
-
# end
-
#
-
# def read_attribute_for_validation(key)
-
# @data[key]
-
# end
-
# end
-
#
-
1
alias :read_attribute_for_validation :send
-
-
1
protected
-
-
1
def run_validations!
-
run_callbacks :validate
-
errors.empty?
-
end
-
end
-
end
-
-
1
Dir[File.dirname(__FILE__) + "/validations/*.rb"].sort.each do |path|
-
11
filename = File.basename(path)
-
11
require "active_model/validations/#{filename}"
-
end
-
1
module ActiveModel
-
-
# == Active Model Acceptance Validator
-
1
module Validations
-
1
class AcceptanceValidator < EachValidator
-
1
def initialize(options)
-
super(options.reverse_merge(:allow_nil => true, :accept => "1"))
-
end
-
-
1
def validate_each(record, attribute, value)
-
unless value == options[:accept]
-
record.errors.add(attribute, :accepted, options.except(:accept, :allow_nil))
-
end
-
end
-
-
1
def setup(klass)
-
attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
-
attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
-
klass.send(:attr_reader, *attr_readers)
-
klass.send(:attr_writer, *attr_writers)
-
end
-
end
-
-
1
module HelperMethods
-
# Encapsulates the pattern of wanting to validate the acceptance of a
-
# terms of service check box (or similar agreement). Example:
-
#
-
# class Person < ActiveRecord::Base
-
# validates_acceptance_of :terms_of_service
-
# validates_acceptance_of :eula, :message => "must be abided"
-
# end
-
#
-
# If the database column does not exist, the +terms_of_service+ attribute
-
# is entirely virtual. This check is performed only if +terms_of_service+
-
# is not +nil+ and by default on save.
-
#
-
# Configuration options:
-
# * <tt>:message</tt> - A custom error message (default is: "must be
-
# accepted").
-
# * <tt>:on</tt> - Specifies when this validation is active. Runs in all
-
# validation contexts by default (+nil+), other options are <tt>:create</tt>
-
# and <tt>:update</tt>.
-
# * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default
-
# is true).
-
# * <tt>:accept</tt> - Specifies value that is considered accepted.
-
# The default value is a string "1", which makes it easy to relate to
-
# an HTML checkbox. This should be set to +true+ if you are validating
-
# a database column, since the attribute is typecast from "1" to +true+
-
# before validation.
-
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
-
# if the validation should occur (e.g. <tt>:if => :allow_validation</tt>,
-
# or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
-
# method, proc or string should return or evaluate to a true or false
-
# value.
-
# * <tt>:unless</tt> - Specifies a method, proc or string to call to
-
# determine if the validation should not occur (for example,
-
# <tt>:unless => :skip_validation</tt>, or
-
# <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>).
-
# The method, proc or string should return or evaluate to a true or
-
# false value.
-
1
def validates_acceptance_of(*attr_names)
-
validates_with AcceptanceValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
require 'active_support/callbacks'
-
-
1
module ActiveModel
-
1
module Validations
-
1
module Callbacks
-
# == Active Model Validation callbacks
-
#
-
# Provides an interface for any class to have <tt>before_validation</tt> and
-
# <tt>after_validation</tt> callbacks.
-
#
-
# First, extend ActiveModel::Callbacks from the class you are creating:
-
#
-
# class MyModel
-
# include ActiveModel::Validations::Callbacks
-
#
-
# before_validation :do_stuff_before_validation
-
# after_validation :do_stuff_after_validation
-
# end
-
#
-
# Like other before_* callbacks if <tt>before_validation</tt> returns false
-
# then <tt>valid?</tt> will not be called.
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
include ActiveSupport::Callbacks
-
1
define_callbacks :validation, :terminator => "result == false", :scope => [:kind, :name]
-
end
-
-
1
module ClassMethods
-
1
def before_validation(*args, &block)
-
options = args.last
-
if options.is_a?(Hash) && options[:on]
-
options[:if] = Array.wrap(options[:if])
-
options[:if].unshift("self.validation_context == :#{options[:on]}")
-
end
-
set_callback(:validation, :before, *args, &block)
-
end
-
-
1
def after_validation(*args, &block)
-
options = args.extract_options!
-
options[:prepend] = true
-
options[:if] = Array.wrap(options[:if])
-
options[:if] << "!halted"
-
options[:if].unshift("self.validation_context == :#{options[:on]}") if options[:on]
-
set_callback(:validation, :after, *(args << options), &block)
-
end
-
end
-
-
1
protected
-
-
# Overwrite run validations to include callbacks.
-
1
def run_validations!
-
run_callbacks(:validation) { super }
-
end
-
end
-
end
-
end
-
1
module ActiveModel
-
-
# == Active Model Confirmation Validator
-
1
module Validations
-
1
class ConfirmationValidator < EachValidator
-
1
def validate_each(record, attribute, value)
-
if (confirmed = record.send("#{attribute}_confirmation")) && (value != confirmed)
-
record.errors.add(attribute, :confirmation, options)
-
end
-
end
-
-
1
def setup(klass)
-
klass.send(:attr_accessor, *attributes.map do |attribute|
-
:"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation")
-
end.compact)
-
end
-
end
-
-
1
module HelperMethods
-
# Encapsulates the pattern of wanting to validate a password or email
-
# address field with a confirmation. For example:
-
#
-
# Model:
-
# class Person < ActiveRecord::Base
-
# validates_confirmation_of :user_name, :password
-
# validates_confirmation_of :email_address,
-
# :message => "should match confirmation"
-
# end
-
#
-
# View:
-
# <%= password_field "person", "password" %>
-
# <%= password_field "person", "password_confirmation" %>
-
#
-
# The added +password_confirmation+ attribute is virtual; it exists only
-
# as an in-memory attribute for validating the password. To achieve this,
-
# the validation adds accessors to the model for the confirmation
-
# attribute.
-
#
-
# NOTE: This check is performed only if +password_confirmation+ is not
-
# +nil+, and by default only on save. To require confirmation, make sure
-
# to add a presence check for the confirmation attribute:
-
#
-
# validates_presence_of :password_confirmation, :if => :password_changed?
-
#
-
# Configuration options:
-
# * <tt>:message</tt> - A custom error message (default is: "doesn't match
-
# confirmation").
-
# * <tt>:on</tt> - Specifies when this validation is active. Runs in all
-
# validation contexts by default (+nil+), other options are <tt>:create</tt>
-
# and <tt>:update</tt>.
-
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
-
# if the validation should occur (e.g. <tt>:if => :allow_validation</tt>,
-
# or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
-
# method, proc or string should return or evaluate to a true or false
-
# value.
-
# * <tt>:unless</tt> - Specifies a method, proc or string to call to
-
# determine if the validation should not occur (e.g.
-
# <tt>:unless => :skip_validation</tt>, or
-
# <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
-
# method, proc or string should return or evaluate to a true or false value.
-
1
def validates_confirmation_of(*attr_names)
-
validates_with ConfirmationValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/range.rb'
-
-
1
module ActiveModel
-
-
# == Active Model Exclusion Validator
-
1
module Validations
-
1
class ExclusionValidator < EachValidator
-
1
ERROR_MESSAGE = "An object with the method #include? or a proc or lambda is required, " <<
-
"and must be supplied as the :in option of the configuration hash"
-
-
1
def check_validity!
-
unless [:include?, :call].any? { |method| options[:in].respond_to?(method) }
-
raise ArgumentError, ERROR_MESSAGE
-
end
-
end
-
-
1
def validate_each(record, attribute, value)
-
delimiter = options[:in]
-
exclusions = delimiter.respond_to?(:call) ? delimiter.call(record) : delimiter
-
if exclusions.send(inclusion_method(exclusions), value)
-
record.errors.add(attribute, :exclusion, options.except(:in).merge!(:value => value))
-
end
-
end
-
-
1
private
-
-
# In Ruby 1.9 <tt>Range#include?</tt> on non-numeric ranges checks all possible values in the
-
# range for equality, so it may be slow for large ranges. The new <tt>Range#cover?</tt>
-
# uses the previous logic of comparing a value with the range endpoints.
-
1
def inclusion_method(enumerable)
-
enumerable.is_a?(Range) ? :cover? : :include?
-
end
-
end
-
-
1
module HelperMethods
-
# Validates that the value of the specified attribute is not in a particular enumerable object.
-
#
-
# class Person < ActiveRecord::Base
-
# validates_exclusion_of :username, :in => %w( admin superuser ), :message => "You don't belong here"
-
# validates_exclusion_of :age, :in => 30..60, :message => "This site is only for under 30 and over 60"
-
# validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension %{value} is not allowed"
-
# validates_exclusion_of :password, :in => lambda { |p| [p.username, p.first_name] }, :message => "should not be the same as your username or first name"
-
# end
-
#
-
# Configuration options:
-
# * <tt>:in</tt> - An enumerable object of items that the value shouldn't be part of.
-
# This can be supplied as a proc or lambda which returns an enumerable. If the enumerable
-
# is a range the test is performed with <tt>Range#cover?</tt>
-
# (backported in Active Support for 1.8), otherwise with <tt>include?</tt>.
-
# * <tt>:message</tt> - Specifies a custom error message (default is: "is reserved").
-
# * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
-
# * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
-
# * <tt>:on</tt> - Specifies when this validation is active. Runs in all
-
# validation contexts by default (+nil+), other options are <tt>:create</tt>
-
# and <tt>:update</tt>.
-
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
-
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
-
# method, proc or string should return or evaluate to a true or false value.
-
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
-
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
-
# method, proc or string should return or evaluate to a true or false value.
-
1
def validates_exclusion_of(*attr_names)
-
validates_with ExclusionValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
module ActiveModel
-
-
# == Active Model Format Validator
-
1
module Validations
-
1
class FormatValidator < EachValidator
-
1
def validate_each(record, attribute, value)
-
if options[:with]
-
regexp = option_call(record, :with)
-
record_error(record, attribute, :with, value) if value.to_s !~ regexp
-
elsif options[:without]
-
regexp = option_call(record, :without)
-
record_error(record, attribute, :without, value) if value.to_s =~ regexp
-
end
-
end
-
-
1
def check_validity!
-
unless options.include?(:with) ^ options.include?(:without) # ^ == xor, or "exclusive or"
-
raise ArgumentError, "Either :with or :without must be supplied (but not both)"
-
end
-
-
check_options_validity(options, :with)
-
check_options_validity(options, :without)
-
end
-
-
1
private
-
-
1
def option_call(record, name)
-
option = options[name]
-
option.respond_to?(:call) ? option.call(record) : option
-
end
-
-
1
def record_error(record, attribute, name, value)
-
record.errors.add(attribute, :invalid, options.except(name).merge!(:value => value))
-
end
-
-
1
def check_options_validity(options, name)
-
option = options[name]
-
if option && !option.is_a?(Regexp) && !option.respond_to?(:call)
-
raise ArgumentError, "A regular expression or a proc or lambda must be supplied as :#{name}"
-
end
-
end
-
end
-
-
1
module HelperMethods
-
# Validates whether the value of the specified attribute is of the correct form, going by the regular expression provided.
-
# You can require that the attribute matches the regular expression:
-
#
-
# class Person < ActiveRecord::Base
-
# validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :create
-
# end
-
#
-
# Alternatively, you can require that the specified attribute does _not_ match the regular expression:
-
#
-
# class Person < ActiveRecord::Base
-
# validates_format_of :email, :without => /NOSPAM/
-
# end
-
#
-
# You can also provide a proc or lambda which will determine the regular expression that will be used to validate the attribute
-
#
-
# class Person < ActiveRecord::Base
-
# # Admin can have number as a first letter in their screen name
-
# validates_format_of :screen_name, :with => lambda{ |person| person.admin? ? /\A[a-z0-9][a-z0-9_\-]*\Z/i : /\A[a-z][a-z0-9_\-]*\Z/i }
-
# end
-
#
-
# Note: use <tt>\A</tt> and <tt>\Z</tt> to match the start and end of the string, <tt>^</tt> and <tt>$</tt> match the start/end of a line.
-
#
-
# You must pass either <tt>:with</tt> or <tt>:without</tt> as an option. In addition, both must be a regular expression
-
# or a proc or lambda, or else an exception will be raised.
-
#
-
# Configuration options:
-
# * <tt>:message</tt> - A custom error message (default is: "is invalid").
-
# * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
-
# * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
-
# * <tt>:with</tt> - Regular expression that if the attribute matches will result in a successful validation.
-
# This can be provided as a proc or lambda returning regular expression which will be called at runtime.
-
# * <tt>:without</tt> - Regular expression that if the attribute does not match will result in a successful validation.
-
# This can be provided as a proc or lambda returning regular expression which will be called at runtime.
-
# * <tt>:on</tt> - Specifies when this validation is active. Runs in all
-
# validation contexts by default (+nil+), other options are <tt>:create</tt>
-
# and <tt>:update</tt>.
-
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
-
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
-
# method, proc or string should return or evaluate to a true or false value.
-
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
-
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
-
# method, proc or string should return or evaluate to a true or false value.
-
1
def validates_format_of(*attr_names)
-
validates_with FormatValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/range.rb'
-
-
1
module ActiveModel
-
-
# == Active Model Inclusion Validator
-
1
module Validations
-
1
class InclusionValidator < EachValidator
-
1
ERROR_MESSAGE = "An object with the method #include? or a proc or lambda is required, " <<
-
"and must be supplied as the :in option of the configuration hash"
-
-
1
def check_validity!
-
6
unless [:include?, :call].any?{ |method| options[:in].respond_to?(method) }
-
raise ArgumentError, ERROR_MESSAGE
-
end
-
end
-
-
1
def validate_each(record, attribute, value)
-
delimiter = options[:in]
-
exclusions = delimiter.respond_to?(:call) ? delimiter.call(record) : delimiter
-
unless exclusions.send(inclusion_method(exclusions), value)
-
record.errors.add(attribute, :inclusion, options.except(:in).merge!(:value => value))
-
end
-
end
-
-
1
private
-
-
# In Ruby 1.9 <tt>Range#include?</tt> on non-numeric ranges checks all possible values in the
-
# range for equality, so it may be slow for large ranges. The new <tt>Range#cover?</tt>
-
# uses the previous logic of comparing a value with the range endpoints.
-
1
def inclusion_method(enumerable)
-
enumerable.is_a?(Range) ? :cover? : :include?
-
end
-
end
-
-
1
module HelperMethods
-
# Validates whether the value of the specified attribute is available in a particular enumerable object.
-
#
-
# class Person < ActiveRecord::Base
-
# validates_inclusion_of :gender, :in => %w( m f )
-
# validates_inclusion_of :age, :in => 0..99
-
# validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension %{value} is not included in the list"
-
# validates_inclusion_of :states, :in => lambda{ |person| STATES[person.country] }
-
# end
-
#
-
# Configuration options:
-
# * <tt>:in</tt> - An enumerable object of available items. This can be
-
# supplied as a proc or lambda which returns an enumerable. If the enumerable
-
# is a range the test is performed with <tt>Range#cover?</tt>
-
# (backported in Active Support for 1.8), otherwise with <tt>include?</tt>.
-
# * <tt>:message</tt> - Specifies a custom error message (default is: "is not included in the list").
-
# * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
-
# * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
-
# * <tt>:on</tt> - Specifies when this validation is active. Runs in all
-
# validation contexts by default (+nil+), other options are <tt>:create</tt>
-
# and <tt>:update</tt>.
-
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
-
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
-
# method, proc or string should return or evaluate to a true or false value.
-
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
-
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
-
# method, proc or string should return or evaluate to a true or false value.
-
1
def validates_inclusion_of(*attr_names)
-
validates_with InclusionValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
module ActiveModel
-
-
# == Active Model Length Validator
-
1
module Validations
-
1
class LengthValidator < EachValidator
-
1
MESSAGES = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long }.freeze
-
1
CHECKS = { :is => :==, :minimum => :>=, :maximum => :<= }.freeze
-
-
1
DEFAULT_TOKENIZER = lambda { |value| value.split(//) }
-
1
RESERVED_OPTIONS = [:minimum, :maximum, :within, :is, :tokenizer, :too_short, :too_long]
-
-
1
def initialize(options)
-
if range = (options.delete(:in) || options.delete(:within))
-
raise ArgumentError, ":in and :within must be a Range" unless range.is_a?(Range)
-
options[:minimum], options[:maximum] = range.begin, range.end
-
options[:maximum] -= 1 if range.exclude_end?
-
end
-
-
super
-
end
-
-
1
def check_validity!
-
keys = CHECKS.keys & options.keys
-
-
if keys.empty?
-
raise ArgumentError, 'Range unspecified. Specify the :within, :maximum, :minimum, or :is option.'
-
end
-
-
keys.each do |key|
-
value = options[key]
-
-
unless value.is_a?(Integer) && value >= 0
-
raise ArgumentError, ":#{key} must be a nonnegative Integer"
-
end
-
end
-
end
-
-
1
def validate_each(record, attribute, value)
-
value = (options[:tokenizer] || DEFAULT_TOKENIZER).call(value) if value.kind_of?(String)
-
-
CHECKS.each do |key, validity_check|
-
next unless check_value = options[key]
-
-
value ||= [] if key == :maximum
-
-
value_length = value.respond_to?(:length) ? value.length : value.to_s.length
-
next if value_length.send(validity_check, check_value)
-
-
errors_options = options.except(*RESERVED_OPTIONS)
-
errors_options[:count] = check_value
-
-
default_message = options[MESSAGES[key]]
-
errors_options[:message] ||= default_message if default_message
-
-
record.errors.add(attribute, MESSAGES[key], errors_options)
-
end
-
end
-
end
-
-
1
module HelperMethods
-
-
# Validates that the specified attribute matches the length restrictions supplied. Only one option can be used at a time:
-
#
-
# class Person < ActiveRecord::Base
-
# validates_length_of :first_name, :maximum => 30
-
# validates_length_of :last_name, :maximum => 30, :message => "less than 30 if you don't mind"
-
# validates_length_of :fax, :in => 7..32, :allow_nil => true
-
# validates_length_of :phone, :in => 7..32, :allow_blank => true
-
# validates_length_of :user_name, :within => 6..20, :too_long => "pick a shorter name", :too_short => "pick a longer name"
-
# validates_length_of :zip_code, :minimum => 5, :too_short => "please enter at least 5 characters"
-
# validates_length_of :smurf_leader, :is => 4, :message => "papa is spelled with 4 characters... don't play me."
-
# validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least 100 words.", :tokenizer => lambda { |str| str.scan(/\w+/) }
-
# end
-
#
-
# Configuration options:
-
# * <tt>:minimum</tt> - The minimum size of the attribute.
-
# * <tt>:maximum</tt> - The maximum size of the attribute.
-
# * <tt>:is</tt> - The exact size of the attribute.
-
# * <tt>:within</tt> - A range specifying the minimum and maximum size of the attribute.
-
# * <tt>:in</tt> - A synonym(or alias) for <tt>:within</tt>.
-
# * <tt>:allow_nil</tt> - Attribute may be +nil+; skip validation.
-
# * <tt>:allow_blank</tt> - Attribute may be blank; skip validation.
-
# * <tt>:too_long</tt> - The error message if the attribute goes over the maximum (default is: "is too long (maximum is %{count} characters)").
-
# * <tt>:too_short</tt> - The error message if the attribute goes under the minimum (default is: "is too short (min is %{count} characters)").
-
# * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt> method and the attribute is the wrong size (default is: "is the wrong length (should be %{count} characters)").
-
# * <tt>:message</tt> - The error message to use for a <tt>:minimum</tt>, <tt>:maximum</tt>, or <tt>:is</tt> violation. An alias of the appropriate <tt>too_long</tt>/<tt>too_short</tt>/<tt>wrong_length</tt> message.
-
# * <tt>:on</tt> - Specifies when this validation is active. Runs in all
-
# validation contexts by default (+nil+), other options are <tt>:create</tt>
-
# and <tt>:update</tt>.
-
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
-
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
-
# method, proc or string should return or evaluate to a true or false value.
-
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
-
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
-
# method, proc or string should return or evaluate to a true or false value.
-
# * <tt>:tokenizer</tt> - Specifies how to split up the attribute string. (e.g. <tt>:tokenizer => lambda {|str| str.scan(/\w+/)}</tt> to
-
# count words as in above example.)
-
# Defaults to <tt>lambda{ |value| value.split(//) }</tt> which counts individual characters.
-
1
def validates_length_of(*attr_names)
-
validates_with LengthValidator, _merge_attributes(attr_names)
-
end
-
-
1
alias_method :validates_size_of, :validates_length_of
-
end
-
end
-
end
-
1
module ActiveModel
-
-
# == Active Model Numericality Validator
-
1
module Validations
-
1
class NumericalityValidator < EachValidator
-
1
CHECKS = { :greater_than => :>, :greater_than_or_equal_to => :>=,
-
:equal_to => :==, :less_than => :<, :less_than_or_equal_to => :<=,
-
:odd => :odd?, :even => :even? }.freeze
-
-
1
RESERVED_OPTIONS = CHECKS.keys + [:only_integer]
-
-
1
def check_validity!
-
keys = CHECKS.keys - [:odd, :even]
-
options.slice(*keys).each do |option, value|
-
next if value.is_a?(Numeric) || value.is_a?(Proc) || value.is_a?(Symbol)
-
raise ArgumentError, ":#{option} must be a number, a symbol or a proc"
-
end
-
end
-
-
1
def validate_each(record, attr_name, value)
-
before_type_cast = "#{attr_name}_before_type_cast"
-
-
raw_value = record.send(before_type_cast) if record.respond_to?(before_type_cast.to_sym)
-
raw_value ||= value
-
-
return if options[:allow_nil] && raw_value.nil?
-
-
unless value = parse_raw_value_as_a_number(raw_value)
-
record.errors.add(attr_name, :not_a_number, filtered_options(raw_value))
-
return
-
end
-
-
if options[:only_integer]
-
unless value = parse_raw_value_as_an_integer(raw_value)
-
record.errors.add(attr_name, :not_an_integer, filtered_options(raw_value))
-
return
-
end
-
end
-
-
options.slice(*CHECKS.keys).each do |option, option_value|
-
case option
-
when :odd, :even
-
unless value.to_i.send(CHECKS[option])
-
record.errors.add(attr_name, option, filtered_options(value))
-
end
-
else
-
option_value = option_value.call(record) if option_value.is_a?(Proc)
-
option_value = record.send(option_value) if option_value.is_a?(Symbol)
-
-
unless value.send(CHECKS[option], option_value)
-
record.errors.add(attr_name, option, filtered_options(value).merge(:count => option_value))
-
end
-
end
-
end
-
end
-
-
1
protected
-
-
1
def parse_raw_value_as_a_number(raw_value)
-
case raw_value
-
when /\A0[xX]/
-
nil
-
else
-
begin
-
Kernel.Float(raw_value)
-
rescue ArgumentError, TypeError
-
nil
-
end
-
end
-
end
-
-
1
def parse_raw_value_as_an_integer(raw_value)
-
raw_value.to_i if raw_value.to_s =~ /\A[+-]?\d+\Z/
-
end
-
-
1
def filtered_options(value)
-
options.except(*RESERVED_OPTIONS).merge!(:value => value)
-
end
-
end
-
-
1
module HelperMethods
-
# Validates whether the value of the specified attribute is numeric by trying to convert it to
-
# a float with Kernel.Float (if <tt>only_integer</tt> is false) or applying it to the regular expression
-
# <tt>/\A[\+\-]?\d+\Z/</tt> (if <tt>only_integer</tt> is set to true).
-
#
-
# class Person < ActiveRecord::Base
-
# validates_numericality_of :value, :on => :create
-
# end
-
#
-
# Configuration options:
-
# * <tt>:message</tt> - A custom error message (default is: "is not a number").
-
# * <tt>:on</tt> - Specifies when this validation is active. Runs in all
-
# validation contexts by default (+nil+), other options are <tt>:create</tt>
-
# and <tt>:update</tt>.
-
# * <tt>:only_integer</tt> - Specifies whether the value has to be an integer, e.g. an integral value (default is +false+).
-
# * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default is +false+). Notice that for fixnum and float columns empty strings are converted to +nil+.
-
# * <tt>:greater_than</tt> - Specifies the value must be greater than the supplied value.
-
# * <tt>:greater_than_or_equal_to</tt> - Specifies the value must be greater than or equal the supplied value.
-
# * <tt>:equal_to</tt> - Specifies the value must be equal to the supplied value.
-
# * <tt>:less_than</tt> - Specifies the value must be less than the supplied value.
-
# * <tt>:less_than_or_equal_to</tt> - Specifies the value must be less than or equal the supplied value.
-
# * <tt>:odd</tt> - Specifies the value must be an odd number.
-
# * <tt>:even</tt> - Specifies the value must be an even number.
-
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
-
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
-
# method, proc or string should return or evaluate to a true or false value.
-
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
-
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
-
# method, proc or string should return or evaluate to a true or false value.
-
#
-
# The following checks can also be supplied with a proc or a symbol which corresponds to a method:
-
# * <tt>:greater_than</tt>
-
# * <tt>:greater_than_or_equal_to</tt>
-
# * <tt>:equal_to</tt>
-
# * <tt>:less_than</tt>
-
# * <tt>:less_than_or_equal_to</tt>
-
#
-
# class Person < ActiveRecord::Base
-
# validates_numericality_of :width, :less_than => Proc.new { |person| person.height }
-
# validates_numericality_of :width, :greater_than => :minimum_weight
-
# end
-
#
-
1
def validates_numericality_of(*attr_names)
-
validates_with NumericalityValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/blank'
-
-
1
module ActiveModel
-
-
# == Active Model Presence Validator
-
1
module Validations
-
1
class PresenceValidator < EachValidator
-
1
def validate(record)
-
record.errors.add_on_blank(attributes, options)
-
end
-
end
-
-
1
module HelperMethods
-
# Validates that the specified attributes are not blank (as defined by Object#blank?). Happens by default on save. Example:
-
#
-
# class Person < ActiveRecord::Base
-
# validates_presence_of :first_name
-
# end
-
#
-
# The first_name attribute must be in the object and it cannot be blank.
-
#
-
# If you want to validate the presence of a boolean field (where the real values are true and false),
-
# you will want to use <tt>validates_inclusion_of :field_name, :in => [true, false]</tt>.
-
#
-
# This is due to the way Object#blank? handles boolean values: <tt>false.blank? # => true</tt>.
-
#
-
# Configuration options:
-
# * <tt>message</tt> - A custom error message (default is: "can't be blank").
-
# * <tt>:on</tt> - Specifies when this validation is active. Runs in all
-
# validation contexts by default (+nil+), other options are <tt>:create</tt>
-
# and <tt>:update</tt>.
-
# * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
-
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>).
-
# The method, proc or string should return or evaluate to a true or false value.
-
# * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
-
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>).
-
# The method, proc or string should return or evaluate to a true or false value.
-
#
-
1
def validates_presence_of(*attr_names)
-
validates_with PresenceValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/slice'
-
-
1
module ActiveModel
-
-
# == Active Model validates method
-
1
module Validations
-
1
module ClassMethods
-
# This method is a shortcut to all default validators and any custom
-
# validator classes ending in 'Validator'. Note that Rails default
-
# validators can be overridden inside specific classes by creating
-
# custom validator classes in their place such as PresenceValidator.
-
#
-
# Examples of using the default rails validators:
-
#
-
# validates :terms, :acceptance => true
-
# validates :password, :confirmation => true
-
# validates :username, :exclusion => { :in => %w(admin superuser) }
-
# validates :email, :format => { :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :create }
-
# validates :age, :inclusion => { :in => 0..9 }
-
# validates :first_name, :length => { :maximum => 30 }
-
# validates :age, :numericality => true
-
# validates :username, :presence => true
-
# validates :username, :uniqueness => true
-
#
-
# The power of the +validates+ method comes when using custom validators
-
# and default validators in one call for a given attribute e.g.
-
#
-
# class EmailValidator < ActiveModel::EachValidator
-
# def validate_each(record, attribute, value)
-
# record.errors[attribute] << (options[:message] || "is not an email") unless
-
# value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
-
# end
-
# end
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# attr_accessor :name, :email
-
#
-
# validates :name, :presence => true, :uniqueness => true, :length => { :maximum => 100 }
-
# validates :email, :presence => true, :email => true
-
# end
-
#
-
# Validator classes may also exist within the class being validated
-
# allowing custom modules of validators to be included as needed e.g.
-
#
-
# class Film
-
# include ActiveModel::Validations
-
#
-
# class TitleValidator < ActiveModel::EachValidator
-
# def validate_each(record, attribute, value)
-
# record.errors[attribute] << "must start with 'the'" unless value =~ /\Athe/i
-
# end
-
# end
-
#
-
# validates :name, :title => true
-
# end
-
#
-
# Additionally validator classes may be in another namespace and still used within any class.
-
#
-
# validates :name, :'file/title' => true
-
#
-
# The validators hash can also handle regular expressions, ranges,
-
# arrays and strings in shortcut form, e.g.
-
#
-
# validates :email, :format => /@/
-
# validates :gender, :inclusion => %w(male female)
-
# validates :password, :length => 6..20
-
#
-
# When using shortcut form, ranges and arrays are passed to your
-
# validator's initializer as +options[:in]+ while other types including
-
# regular expressions and strings are passed as +options[:with]+
-
#
-
# Finally, the options +:if+, +:unless+, +:on+, +:allow_blank+ and +:allow_nil+ can be given
-
# to one specific validator, as a hash:
-
#
-
# validates :password, :presence => { :if => :password_required? }, :confirmation => true
-
#
-
# Or to all at the same time:
-
#
-
# validates :password, :presence => true, :confirmation => true, :if => :password_required?
-
#
-
1
def validates(*attributes)
-
8
defaults = attributes.extract_options!
-
8
validations = defaults.slice!(*_validates_default_keys)
-
-
8
raise ArgumentError, "You need to supply at least one attribute" if attributes.empty?
-
8
raise ArgumentError, "You need to supply at least one validation" if validations.empty?
-
-
8
defaults.merge!(:attributes => attributes)
-
-
8
validations.each do |key, options|
-
8
key = "#{key.to_s.camelize}Validator"
-
-
8
begin
-
8
validator = key.include?('::') ? key.constantize : const_get(key)
-
rescue NameError
-
raise ArgumentError, "Unknown validator: '#{key}'"
-
end
-
-
8
validates_with(validator, defaults.merge(_parse_validates_options(options)))
-
end
-
end
-
-
1
protected
-
-
# When creating custom validators, it might be useful to be able to specify
-
# additional default keys. This can be done by overwriting this method.
-
1
def _validates_default_keys
-
8
[ :if, :unless, :on, :allow_blank, :allow_nil ]
-
end
-
-
1
def _parse_validates_options(options) #:nodoc:
-
8
case options
-
when TrueClass
-
5
{}
-
when Hash
-
3
options
-
when Range, Array
-
{ :in => options }
-
else
-
{ :with => options }
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveModel
-
1
module Validations
-
1
module HelperMethods
-
1
private
-
1
def _merge_attributes(attr_names)
-
1
options = attr_names.extract_options!
-
1
options.merge(:attributes => attr_names.flatten)
-
end
-
end
-
-
1
class WithValidator < EachValidator
-
1
def validate_each(record, attr, val)
-
method_name = options[:with]
-
-
if record.method(method_name).arity == 0
-
record.send method_name
-
else
-
record.send method_name, attr
-
end
-
end
-
end
-
-
1
module ClassMethods
-
# Passes the record off to the class or classes specified and allows them
-
# to add errors based on more complex conditions.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# validates_with MyValidator
-
# end
-
#
-
# class MyValidator < ActiveModel::Validator
-
# def validate(record)
-
# if some_complex_logic
-
# record.errors[:base] << "This record is invalid"
-
# end
-
# end
-
#
-
# private
-
# def some_complex_logic
-
# # ...
-
# end
-
# end
-
#
-
# You may also pass it multiple classes, like so:
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# validates_with MyValidator, MyOtherValidator, :on => :create
-
# end
-
#
-
# Configuration options:
-
# * <tt>:on</tt> - Specifies when this validation is active
-
# (<tt>:create</tt> or <tt>:update</tt>
-
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
-
# if the validation should occur (e.g. <tt>:if => :allow_validation</tt>,
-
# or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>).
-
# The method, proc or string should return or evaluate to a true or false value.
-
# * <tt>unless</tt> - Specifies a method, proc or string to call to
-
# determine if the validation should not occur
-
# (e.g. <tt>:unless => :skip_validation</tt>, or
-
# <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>).
-
# The method, proc or string should return or evaluate to a true or false value.
-
#
-
# If you pass any additional configuration options, they will be passed
-
# to the class and available as <tt>options</tt>:
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# validates_with MyValidator, :my_custom_key => "my custom value"
-
# end
-
#
-
# class MyValidator < ActiveModel::Validator
-
# def validate(record)
-
# options[:my_custom_key] # => "my custom value"
-
# end
-
# end
-
#
-
1
def validates_with(*args, &block)
-
9
options = args.extract_options!
-
9
args.each do |klass|
-
9
validator = klass.new(options, &block)
-
9
validator.setup(self) if validator.respond_to?(:setup)
-
-
9
if validator.respond_to?(:attributes) && !validator.attributes.empty?
-
9
validator.attributes.each do |attribute|
-
9
_validators[attribute.to_sym] << validator
-
end
-
else
-
_validators[nil] << validator
-
end
-
-
9
validate(validator, options)
-
end
-
end
-
end
-
-
# Passes the record off to the class or classes specified and allows them
-
# to add errors based on more complex conditions.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# validate :instance_validations
-
#
-
# def instance_validations
-
# validates_with MyValidator
-
# end
-
# end
-
#
-
# Please consult the class method documentation for more information on
-
# creating your own validator.
-
#
-
# You may also pass it multiple classes, like so:
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# validate :instance_validations, :on => :create
-
#
-
# def instance_validations
-
# validates_with MyValidator, MyOtherValidator
-
# end
-
# end
-
#
-
# Standard configuration options (:on, :if and :unless), which are
-
# available on the class version of validates_with, should instead be
-
# placed on the <tt>validates</tt> method as these are applied and tested
-
# in the callback
-
#
-
# If you pass any additional configuration options, they will be passed
-
# to the class and available as <tt>options</tt>, please refer to the
-
# class version of this method for more information
-
#
-
1
def validates_with(*args, &block)
-
options = args.extract_options!
-
args.each do |klass|
-
validator = klass.new(options, &block)
-
validator.validate(self)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/array/wrap'
-
1
require "active_support/core_ext/module/anonymous"
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/object/inclusion'
-
-
1
module ActiveModel #:nodoc:
-
-
# == Active Model Validator
-
#
-
# A simple base class that can be used along with
-
# ActiveModel::Validations::ClassMethods.validates_with
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# validates_with MyValidator
-
# end
-
#
-
# class MyValidator < ActiveModel::Validator
-
# def validate(record)
-
# if some_complex_logic
-
# record.errors[:base] = "This record is invalid"
-
# end
-
# end
-
#
-
# private
-
# def some_complex_logic
-
# # ...
-
# end
-
# end
-
#
-
# Any class that inherits from ActiveModel::Validator must implement a method
-
# called <tt>validate</tt> which accepts a <tt>record</tt>.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# validates_with MyValidator
-
# end
-
#
-
# class MyValidator < ActiveModel::Validator
-
# def validate(record)
-
# record # => The person instance being validated
-
# options # => Any non-standard options passed to validates_with
-
# end
-
# end
-
#
-
# To cause a validation error, you must add to the <tt>record</tt>'s errors directly
-
# from within the validators message
-
#
-
# class MyValidator < ActiveModel::Validator
-
# def validate(record)
-
# record.errors[:base] << "This is some custom error message"
-
# record.errors[:first_name] << "This is some complex validation"
-
# # etc...
-
# end
-
# end
-
#
-
# To add behavior to the initialize method, use the following signature:
-
#
-
# class MyValidator < ActiveModel::Validator
-
# def initialize(record, options)
-
# super
-
# @my_custom_field = options[:field_name] || :first_name
-
# end
-
# end
-
#
-
# The easiest way to add custom validators for validating individual attributes
-
# is with the convenient <tt>ActiveModel::EachValidator</tt>. For example:
-
#
-
# class TitleValidator < ActiveModel::EachValidator
-
# def validate_each(record, attribute, value)
-
# record.errors[attribute] << 'must be Mr. Mrs. or Dr.' unless value.in?(['Mr.', 'Mrs.', 'Dr.'])
-
# end
-
# end
-
#
-
# This can now be used in combination with the +validates+ method
-
# (see <tt>ActiveModel::Validations::ClassMethods.validates</tt> for more on this)
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# attr_accessor :title
-
#
-
# validates :title, :presence => true
-
# end
-
#
-
# Validator may also define a +setup+ instance method which will get called
-
# with the class that using that validator as its argument. This can be
-
# useful when there are prerequisites such as an +attr_accessor+ being present
-
# for example:
-
#
-
# class MyValidator < ActiveModel::Validator
-
# def setup(klass)
-
# klass.send :attr_accessor, :custom_attribute
-
# end
-
# end
-
#
-
# This setup method is only called when used with validation macros or the
-
# class level <tt>validates_with</tt> method.
-
#
-
1
class Validator
-
1
attr_reader :options
-
-
# Returns the kind of the validator. Examples:
-
#
-
# PresenceValidator.kind # => :presence
-
# UniquenessValidator.kind # => :uniqueness
-
#
-
1
def self.kind
-
@kind ||= name.split('::').last.underscore.sub(/_validator$/, '').to_sym unless anonymous?
-
end
-
-
# Accepts options that will be made available through the +options+ reader.
-
1
def initialize(options)
-
9
@options = options.freeze
-
end
-
-
# Return the kind for this validator.
-
1
def kind
-
self.class.kind
-
end
-
-
# Override this method in subclasses with validation logic, adding errors
-
# to the records +errors+ array where necessary.
-
1
def validate(record)
-
raise NotImplementedError, "Subclasses must implement a validate(record) method."
-
end
-
end
-
-
# +EachValidator+ is a validator which iterates through the attributes given
-
# in the options hash invoking the <tt>validate_each</tt> method passing in the
-
# record, attribute and value.
-
#
-
# All Active Model validations are built on top of this validator.
-
1
class EachValidator < Validator
-
1
attr_reader :attributes
-
-
# Returns a new validator instance. All options will be available via the
-
# +options+ reader, however the <tt>:attributes</tt> option will be removed
-
# and instead be made available through the +attributes+ reader.
-
1
def initialize(options)
-
9
@attributes = Array.wrap(options.delete(:attributes))
-
9
raise ":attributes cannot be blank" if @attributes.empty?
-
9
super
-
9
check_validity!
-
end
-
-
# Performs validation on the supplied record. By default this will call
-
# +validates_each+ to determine validity therefore subclasses should
-
# override +validates_each+ with validation logic.
-
1
def validate(record)
-
attributes.each do |attribute|
-
value = record.read_attribute_for_validation(attribute)
-
next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
-
validate_each(record, attribute, value)
-
end
-
end
-
-
# Override this method in subclasses with the validation logic, adding
-
# errors to the records +errors+ array where necessary.
-
1
def validate_each(record, attribute, value)
-
raise NotImplementedError, "Subclasses must implement a validate_each(record, attribute, value) method"
-
end
-
-
# Hook method that gets called by the initializer allowing verification
-
# that the arguments supplied are valid. You could for example raise an
-
# +ArgumentError+ when invalid options are supplied.
-
1
def check_validity!
-
end
-
end
-
-
# +BlockValidator+ is a special +EachValidator+ which receives a block on initialization
-
# and call this block for each attribute being validated. +validates_each+ uses this validator.
-
1
class BlockValidator < EachValidator
-
1
def initialize(options, &block)
-
@block = block
-
super
-
end
-
-
1
private
-
-
1
def validate_each(record, attribute, value)
-
@block.call(record, attribute, value)
-
end
-
end
-
end
-
#--
-
# Copyright (c) 2004-2011 David Heinemeier Hansson
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining
-
# a copy of this software and associated documentation files (the
-
# "Software"), to deal in the Software without restriction, including
-
# without limitation the rights to use, copy, modify, merge, publish,
-
# distribute, sublicense, and/or sell copies of the Software, and to
-
# permit persons to whom the Software is furnished to do so, subject to
-
# the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be
-
# included in all copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
#++
-
-
-
1
activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
-
1
$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
-
-
1
activemodel_path = File.expand_path('../../../activemodel/lib', __FILE__)
-
1
$:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include?(activemodel_path)
-
-
1
require 'active_support'
-
1
require 'active_support/i18n'
-
1
require 'active_model'
-
1
require 'arel'
-
-
1
require 'active_record/version'
-
-
1
module ActiveRecord
-
1
extend ActiveSupport::Autoload
-
-
1
eager_autoload do
-
1
autoload :ActiveRecordError, 'active_record/errors'
-
1
autoload :ConnectionNotEstablished, 'active_record/errors'
-
-
1
autoload :Aggregations
-
1
autoload :Associations
-
1
autoload :AttributeMethods
-
1
autoload :AutosaveAssociation
-
-
1
autoload :Relation
-
-
1
autoload_under 'relation' do
-
1
autoload :QueryMethods
-
1
autoload :FinderMethods
-
1
autoload :Calculations
-
1
autoload :PredicateBuilder
-
1
autoload :SpawnMethods
-
1
autoload :Batches
-
end
-
-
1
autoload :Base
-
1
autoload :Callbacks
-
1
autoload :CounterCache
-
1
autoload :DynamicFinderMatch
-
1
autoload :DynamicScopeMatch
-
1
autoload :Migration
-
1
autoload :Migrator, 'active_record/migration'
-
1
autoload :NamedScope
-
1
autoload :NestedAttributes
-
1
autoload :Observer
-
1
autoload :Persistence
-
1
autoload :QueryCache
-
1
autoload :Reflection
-
1
autoload :Schema
-
1
autoload :SchemaDumper
-
1
autoload :Serialization
-
1
autoload :SessionStore
-
1
autoload :Timestamp
-
1
autoload :Transactions
-
1
autoload :Validations
-
1
autoload :IdentityMap
-
end
-
-
1
module Coders
-
1
autoload :YAMLColumn, 'active_record/coders/yaml_column'
-
end
-
-
1
module AttributeMethods
-
1
extend ActiveSupport::Autoload
-
-
1
eager_autoload do
-
1
autoload :BeforeTypeCast
-
1
autoload :Dirty
-
1
autoload :PrimaryKey
-
1
autoload :Query
-
1
autoload :Read
-
1
autoload :TimeZoneConversion
-
1
autoload :Write
-
end
-
end
-
-
1
module Locking
-
1
extend ActiveSupport::Autoload
-
-
1
eager_autoload do
-
1
autoload :Optimistic
-
1
autoload :Pessimistic
-
end
-
end
-
-
1
module ConnectionAdapters
-
1
extend ActiveSupport::Autoload
-
-
1
eager_autoload do
-
1
autoload :AbstractAdapter
-
1
autoload :ConnectionManagement, "active_record/connection_adapters/abstract/connection_pool"
-
end
-
end
-
-
1
autoload :TestCase
-
1
autoload :TestFixtures, 'active_record/fixtures'
-
end
-
-
1
ActiveSupport.on_load(:active_record) do
-
1
Arel::Table.engine = self
-
end
-
-
1
I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
-
1
module ActiveRecord
-
# = Active Record Aggregations
-
1
module Aggregations # :nodoc:
-
1
extend ActiveSupport::Concern
-
-
1
def clear_aggregation_cache #:nodoc:
-
@aggregation_cache.clear if persisted?
-
end
-
-
# Active Record implements aggregation through a macro-like class method called +composed_of+
-
# for representing attributes as value objects. It expresses relationships like "Account [is]
-
# composed of Money [among other things]" or "Person [is] composed of [an] address". Each call
-
# to the macro adds a description of how the value objects are created from the attributes of
-
# the entity object (when the entity is initialized either as a new object or from finding an
-
# existing object) and how it can be turned back into attributes (when the entity is saved to
-
# the database).
-
#
-
# class Customer < ActiveRecord::Base
-
# composed_of :balance, :class_name => "Money", :mapping => %w(balance amount)
-
# composed_of :address, :mapping => [ %w(address_street street), %w(address_city city) ]
-
# end
-
#
-
# The customer class now has the following methods to manipulate the value objects:
-
# * <tt>Customer#balance, Customer#balance=(money)</tt>
-
# * <tt>Customer#address, Customer#address=(address)</tt>
-
#
-
# These methods will operate with value objects like the ones described below:
-
#
-
# class Money
-
# include Comparable
-
# attr_reader :amount, :currency
-
# EXCHANGE_RATES = { "USD_TO_DKK" => 6 }
-
#
-
# def initialize(amount, currency = "USD")
-
# @amount, @currency = amount, currency
-
# end
-
#
-
# def exchange_to(other_currency)
-
# exchanged_amount = (amount * EXCHANGE_RATES["#{currency}_TO_#{other_currency}"]).floor
-
# Money.new(exchanged_amount, other_currency)
-
# end
-
#
-
# def ==(other_money)
-
# amount == other_money.amount && currency == other_money.currency
-
# end
-
#
-
# def <=>(other_money)
-
# if currency == other_money.currency
-
# amount <=> amount
-
# else
-
# amount <=> other_money.exchange_to(currency).amount
-
# end
-
# end
-
# end
-
#
-
# class Address
-
# attr_reader :street, :city
-
# def initialize(street, city)
-
# @street, @city = street, city
-
# end
-
#
-
# def close_to?(other_address)
-
# city == other_address.city
-
# end
-
#
-
# def ==(other_address)
-
# city == other_address.city && street == other_address.street
-
# end
-
# end
-
#
-
# Now it's possible to access attributes from the database through the value objects instead. If
-
# you choose to name the composition the same as the attribute's name, it will be the only way to
-
# access that attribute. That's the case with our +balance+ attribute. You interact with the value
-
# objects just like you would any other attribute, though:
-
#
-
# customer.balance = Money.new(20) # sets the Money value object and the attribute
-
# customer.balance # => Money value object
-
# customer.balance.exchange_to("DKK") # => Money.new(120, "DKK")
-
# customer.balance > Money.new(10) # => true
-
# customer.balance == Money.new(20) # => true
-
# customer.balance < Money.new(5) # => false
-
#
-
# Value objects can also be composed of multiple attributes, such as the case of Address. The order
-
# of the mappings will determine the order of the parameters.
-
#
-
# customer.address_street = "Hyancintvej"
-
# customer.address_city = "Copenhagen"
-
# customer.address # => Address.new("Hyancintvej", "Copenhagen")
-
# customer.address = Address.new("May Street", "Chicago")
-
# customer.address_street # => "May Street"
-
# customer.address_city # => "Chicago"
-
#
-
# == Writing value objects
-
#
-
# Value objects are immutable and interchangeable objects that represent a given value, such as
-
# a Money object representing $5. Two Money objects both representing $5 should be equal (through
-
# methods such as <tt>==</tt> and <tt><=></tt> from Comparable if ranking makes sense). This is
-
# unlike entity objects where equality is determined by identity. An entity class such as Customer can
-
# easily have two different objects that both have an address on Hyancintvej. Entity identity is
-
# determined by object or relational unique identifiers (such as primary keys). Normal
-
# ActiveRecord::Base classes are entity objects.
-
#
-
# It's also important to treat the value objects as immutable. Don't allow the Money object to have
-
# its amount changed after creation. Create a new Money object with the new value instead. This
-
# is exemplified by the Money#exchange_to method that returns a new value object instead of changing
-
# its own values. Active Record won't persist value objects that have been changed through means
-
# other than the writer method.
-
#
-
# The immutable requirement is enforced by Active Record by freezing any object assigned as a value
-
# object. Attempting to change it afterwards will result in a ActiveSupport::FrozenObjectError.
-
#
-
# Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not
-
# keeping value objects immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
-
#
-
# == Custom constructors and converters
-
#
-
# By default value objects are initialized by calling the <tt>new</tt> constructor of the value
-
# class passing each of the mapped attributes, in the order specified by the <tt>:mapping</tt>
-
# option, as arguments. If the value class doesn't support this convention then +composed_of+ allows
-
# a custom constructor to be specified.
-
#
-
# When a new value is assigned to the value object the default assumption is that the new value
-
# is an instance of the value class. Specifying a custom converter allows the new value to be automatically
-
# converted to an instance of value class if necessary.
-
#
-
# For example, the NetworkResource model has +network_address+ and +cidr_range+ attributes that
-
# should be aggregated using the NetAddr::CIDR value class (http://netaddr.rubyforge.org). The constructor
-
# for the value class is called +create+ and it expects a CIDR address string as a parameter. New
-
# values can be assigned to the value object using either another NetAddr::CIDR object, a string
-
# or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet
-
# these requirements:
-
#
-
# class NetworkResource < ActiveRecord::Base
-
# composed_of :cidr,
-
# :class_name => 'NetAddr::CIDR',
-
# :mapping => [ %w(network_address network), %w(cidr_range bits) ],
-
# :allow_nil => true,
-
# :constructor => Proc.new { |network_address, cidr_range| NetAddr::CIDR.create("#{network_address}/#{cidr_range}") },
-
# :converter => Proc.new { |value| NetAddr::CIDR.create(value.is_a?(Array) ? value.join('/') : value) }
-
# end
-
#
-
# # This calls the :constructor
-
# network_resource = NetworkResource.new(:network_address => '192.168.0.1', :cidr_range => 24)
-
#
-
# # These assignments will both use the :converter
-
# network_resource.cidr = [ '192.168.2.1', 8 ]
-
# network_resource.cidr = '192.168.0.1/24'
-
#
-
# # This assignment won't use the :converter as the value is already an instance of the value class
-
# network_resource.cidr = NetAddr::CIDR.create('192.168.2.1/8')
-
#
-
# # Saving and then reloading will use the :constructor on reload
-
# network_resource.save
-
# network_resource.reload
-
#
-
# == Finding records by a value object
-
#
-
# Once a +composed_of+ relationship is specified for a model, records can be loaded from the database
-
# by specifying an instance of the value object in the conditions hash. The following example
-
# finds all customers with +balance_amount+ equal to 20 and +balance_currency+ equal to "USD":
-
#
-
# Customer.where(:balance => Money.new(20, "USD")).all
-
#
-
1
module ClassMethods
-
# Adds reader and writer methods for manipulating a value object:
-
# <tt>composed_of :address</tt> adds <tt>address</tt> and <tt>address=(new_address)</tt> methods.
-
#
-
# Options are:
-
# * <tt>:class_name</tt> - Specifies the class name of the association. Use it only if that name
-
# can't be inferred from the part id. So <tt>composed_of :address</tt> will by default be linked
-
# to the Address class, but if the real class name is CompanyAddress, you'll have to specify it
-
# with this option.
-
# * <tt>:mapping</tt> - Specifies the mapping of entity attributes to attributes of the value
-
# object. Each mapping is represented as an array where the first item is the name of the
-
# entity attribute and the second item is the name the attribute in the value object. The
-
# order in which mappings are defined determine the order in which attributes are sent to the
-
# value class constructor.
-
# * <tt>:allow_nil</tt> - Specifies that the value object will not be instantiated when all mapped
-
# attributes are +nil+. Setting the value object to +nil+ has the effect of writing +nil+ to all
-
# mapped attributes.
-
# This defaults to +false+.
-
# * <tt>:constructor</tt> - A symbol specifying the name of the constructor method or a Proc that
-
# is called to initialize the value object. The constructor is passed all of the mapped attributes,
-
# in the order that they are defined in the <tt>:mapping option</tt>, as arguments and uses them
-
# to instantiate a <tt>:class_name</tt> object.
-
# The default is <tt>:new</tt>.
-
# * <tt>:converter</tt> - A symbol specifying the name of a class method of <tt>:class_name</tt>
-
# or a Proc that is called when a new value is assigned to the value object. The converter is
-
# passed the single value that is used in the assignment and is only called if the new value is
-
# not an instance of <tt>:class_name</tt>.
-
#
-
# Option examples:
-
# composed_of :temperature, :mapping => %w(reading celsius)
-
# composed_of :balance, :class_name => "Money", :mapping => %w(balance amount), :converter => Proc.new { |balance| balance.to_money }
-
# composed_of :address, :mapping => [ %w(address_street street), %w(address_city city) ]
-
# composed_of :gps_location
-
# composed_of :gps_location, :allow_nil => true
-
# composed_of :ip_address,
-
# :class_name => 'IPAddr',
-
# :mapping => %w(ip to_i),
-
# :constructor => Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) },
-
# :converter => Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) }
-
#
-
1
def composed_of(part_id, options = {})
-
options.assert_valid_keys(:class_name, :mapping, :allow_nil, :constructor, :converter)
-
-
name = part_id.id2name
-
class_name = options[:class_name] || name.camelize
-
mapping = options[:mapping] || [ name, name ]
-
mapping = [ mapping ] unless mapping.first.is_a?(Array)
-
allow_nil = options[:allow_nil] || false
-
constructor = options[:constructor] || :new
-
converter = options[:converter]
-
-
reader_method(name, class_name, mapping, allow_nil, constructor)
-
writer_method(name, class_name, mapping, allow_nil, converter)
-
-
create_reflection(:composed_of, part_id, options, self)
-
end
-
-
1
private
-
1
def reader_method(name, class_name, mapping, allow_nil, constructor)
-
define_method(name) do
-
if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? {|pair| !read_attribute(pair.first).nil? })
-
attrs = mapping.collect {|pair| read_attribute(pair.first)}
-
object = constructor.respond_to?(:call) ?
-
constructor.call(*attrs) :
-
class_name.constantize.send(constructor, *attrs)
-
@aggregation_cache[name] = object
-
end
-
@aggregation_cache[name]
-
end
-
end
-
-
1
def writer_method(name, class_name, mapping, allow_nil, converter)
-
define_method("#{name}=") do |part|
-
if part.nil? && allow_nil
-
mapping.each { |pair| self[pair.first] = nil }
-
@aggregation_cache[name] = nil
-
else
-
unless part.is_a?(class_name.constantize) || converter.nil?
-
part = converter.respond_to?(:call) ?
-
converter.call(part) :
-
class_name.constantize.send(converter, part)
-
end
-
-
mapping.each { |pair| self[pair.first] = part.send(pair.last) }
-
@aggregation_cache[name] = part.freeze
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_support/core_ext/enumerable'
-
1
require 'active_support/core_ext/module/delegation'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/string/conversions'
-
1
require 'active_support/core_ext/module/remove_method'
-
1
require 'active_support/core_ext/class/attribute'
-
-
1
module ActiveRecord
-
1
class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
-
1
def initialize(reflection, associated_class = nil)
-
super("Could not find the inverse association for #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{associated_class.nil? ? reflection.class_name : associated_class.name})")
-
end
-
end
-
-
1
class HasManyThroughAssociationNotFoundError < ActiveRecordError #:nodoc:
-
1
def initialize(owner_class_name, reflection)
-
super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class_name}")
-
end
-
end
-
-
1
class HasManyThroughAssociationPolymorphicSourceError < ActiveRecordError #:nodoc:
-
1
def initialize(owner_class_name, reflection, source_reflection)
-
super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' on the polymorphic object '#{source_reflection.class_name}##{source_reflection.name}'.")
-
end
-
end
-
-
1
class HasManyThroughAssociationPolymorphicThroughError < ActiveRecordError #:nodoc:
-
1
def initialize(owner_class_name, reflection)
-
super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' which goes through the polymorphic association '#{owner_class_name}##{reflection.through_reflection.name}'.")
-
end
-
end
-
-
1
class HasManyThroughAssociationPointlessSourceTypeError < ActiveRecordError #:nodoc:
-
1
def initialize(owner_class_name, reflection, source_reflection)
-
super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' with a :source_type option if the '#{reflection.through_reflection.class_name}##{source_reflection.name}' is not polymorphic. Try removing :source_type on your association.")
-
end
-
end
-
-
1
class HasOneThroughCantAssociateThroughCollection < ActiveRecordError #:nodoc:
-
1
def initialize(owner_class_name, reflection, through_reflection)
-
super("Cannot have a has_one :through association '#{owner_class_name}##{reflection.name}' where the :through association '#{owner_class_name}##{through_reflection.name}' is a collection. Specify a has_one or belongs_to association in the :through option instead.")
-
end
-
end
-
-
1
class HasManyThroughSourceAssociationNotFoundError < ActiveRecordError #:nodoc:
-
1
def initialize(reflection)
-
through_reflection = reflection.through_reflection
-
source_reflection_names = reflection.source_reflection_names
-
source_associations = reflection.through_reflection.klass.reflect_on_all_associations.collect { |a| a.name.inspect }
-
super("Could not find the source association(s) #{source_reflection_names.collect{ |a| a.inspect }.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)}?")
-
end
-
end
-
-
1
class HasManyThroughCantAssociateThroughHasOneOrManyReflection < ActiveRecordError #:nodoc:
-
1
def initialize(owner, reflection)
-
super("Cannot modify association '#{owner.class.name}##{reflection.name}' because the source reflection class '#{reflection.source_reflection.class_name}' is associated to '#{reflection.through_reflection.class_name}' via :#{reflection.source_reflection.macro}.")
-
end
-
end
-
-
1
class HasManyThroughCantAssociateNewRecords < ActiveRecordError #:nodoc:
-
1
def initialize(owner, reflection)
-
super("Cannot associate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to create the has_many :through record associating them.")
-
end
-
end
-
-
1
class HasManyThroughCantDissociateNewRecords < ActiveRecordError #:nodoc:
-
1
def initialize(owner, reflection)
-
super("Cannot dissociate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to delete the has_many :through record associating them.")
-
end
-
end
-
-
1
class HasManyThroughNestedAssociationsAreReadonly < ActiveRecordError #:nodoc
-
1
def initialize(owner, reflection)
-
super("Cannot modify association '#{owner.class.name}##{reflection.name}' because it goes through more than one other association.")
-
end
-
end
-
-
1
class HasAndBelongsToManyAssociationWithPrimaryKeyError < ActiveRecordError #:nodoc:
-
1
def initialize(reflection)
-
super("Primary key is not allowed in a has_and_belongs_to_many join table (#{reflection.options[:join_table]}).")
-
end
-
end
-
-
1
class HasAndBelongsToManyAssociationForeignKeyNeeded < ActiveRecordError #:nodoc:
-
1
def initialize(reflection)
-
super("Cannot create self referential has_and_belongs_to_many association on '#{reflection.class_name rescue nil}##{reflection.name rescue nil}'. :association_foreign_key cannot be the same as the :foreign_key.")
-
end
-
end
-
-
1
class EagerLoadPolymorphicError < ActiveRecordError #:nodoc:
-
1
def initialize(reflection)
-
super("Can not eagerly load the polymorphic association #{reflection.name.inspect}")
-
end
-
end
-
-
1
class ReadOnlyAssociation < ActiveRecordError #:nodoc:
-
1
def initialize(reflection)
-
super("Can not add to a has_many :through association. Try adding to #{reflection.through_reflection.name.inspect}.")
-
end
-
end
-
-
# This error is raised when trying to destroy a parent instance in N:1 or 1:1 associations
-
# (has_many, has_one) when there is at least 1 child associated instance.
-
# ex: if @project.tasks.size > 0, DeleteRestrictionError will be raised when trying to destroy @project
-
1
class DeleteRestrictionError < ActiveRecordError #:nodoc:
-
1
def initialize(name)
-
super("Cannot delete record because of dependent #{name}")
-
end
-
end
-
-
# See ActiveRecord::Associations::ClassMethods for documentation.
-
1
module Associations # :nodoc:
-
1
extend ActiveSupport::Concern
-
-
# These classes will be loaded when associations are created.
-
# So there is no need to eager load them.
-
1
autoload :Association, 'active_record/associations/association'
-
1
autoload :SingularAssociation, 'active_record/associations/singular_association'
-
1
autoload :CollectionAssociation, 'active_record/associations/collection_association'
-
1
autoload :CollectionProxy, 'active_record/associations/collection_proxy'
-
1
autoload :AssociationCollection, 'active_record/associations/collection_proxy'
-
-
1
autoload :BelongsToAssociation, 'active_record/associations/belongs_to_association'
-
1
autoload :BelongsToPolymorphicAssociation, 'active_record/associations/belongs_to_polymorphic_association'
-
1
autoload :HasAndBelongsToManyAssociation, 'active_record/associations/has_and_belongs_to_many_association'
-
1
autoload :HasManyAssociation, 'active_record/associations/has_many_association'
-
1
autoload :HasManyThroughAssociation, 'active_record/associations/has_many_through_association'
-
1
autoload :HasOneAssociation, 'active_record/associations/has_one_association'
-
1
autoload :HasOneThroughAssociation, 'active_record/associations/has_one_through_association'
-
1
autoload :ThroughAssociation, 'active_record/associations/through_association'
-
-
1
module Builder #:nodoc:
-
1
autoload :Association, 'active_record/associations/builder/association'
-
1
autoload :SingularAssociation, 'active_record/associations/builder/singular_association'
-
1
autoload :CollectionAssociation, 'active_record/associations/builder/collection_association'
-
-
1
autoload :BelongsTo, 'active_record/associations/builder/belongs_to'
-
1
autoload :HasOne, 'active_record/associations/builder/has_one'
-
1
autoload :HasMany, 'active_record/associations/builder/has_many'
-
1
autoload :HasAndBelongsToMany, 'active_record/associations/builder/has_and_belongs_to_many'
-
end
-
-
1
autoload :Preloader, 'active_record/associations/preloader'
-
1
autoload :JoinDependency, 'active_record/associations/join_dependency'
-
1
autoload :AssociationScope, 'active_record/associations/association_scope'
-
1
autoload :AliasTracker, 'active_record/associations/alias_tracker'
-
1
autoload :JoinHelper, 'active_record/associations/join_helper'
-
-
# Clears out the association cache.
-
1
def clear_association_cache #:nodoc:
-
@association_cache.clear if persisted?
-
end
-
-
# :nodoc:
-
1
attr_reader :association_cache
-
-
# Returns the association instance for the given name, instantiating it if it doesn't already exist
-
1
def association(name) #:nodoc:
-
association = association_instance_get(name)
-
-
if association.nil?
-
reflection = self.class.reflect_on_association(name)
-
association = reflection.association_class.new(self, reflection)
-
association_instance_set(name, association)
-
end
-
-
association
-
end
-
-
1
private
-
# Returns the specified association instance if it responds to :loaded?, nil otherwise.
-
1
def association_instance_get(name)
-
@association_cache[name.to_sym]
-
end
-
-
# Set the specified association instance.
-
1
def association_instance_set(name, association)
-
@association_cache[name] = association
-
end
-
-
# Associations are a set of macro-like class methods for tying objects together through
-
# foreign keys. They express relationships like "Project has one Project Manager"
-
# or "Project belongs to a Portfolio". Each macro adds a number of methods to the
-
# class which are specialized according to the collection or association symbol and the
-
# options hash. It works much the same way as Ruby's own <tt>attr*</tt>
-
# methods.
-
#
-
# class Project < ActiveRecord::Base
-
# belongs_to :portfolio
-
# has_one :project_manager
-
# has_many :milestones
-
# has_and_belongs_to_many :categories
-
# end
-
#
-
# The project class now has the following methods (and more) to ease the traversal and
-
# manipulation of its relationships:
-
# * <tt>Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil?</tt>
-
# * <tt>Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?,</tt>
-
# * <tt>Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone),</tt>
-
# <tt>Project#milestones.delete(milestone), Project#milestones.find(milestone_id), Project#milestones.find(:all, options),</tt>
-
# <tt>Project#milestones.build, Project#milestones.create</tt>
-
# * <tt>Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1),</tt>
-
# <tt>Project#categories.delete(category1)</tt>
-
#
-
# === A word of warning
-
#
-
# Don't create associations that have the same name as instance methods of
-
# <tt>ActiveRecord::Base</tt>. Since the association adds a method with that name to
-
# its model, it will override the inherited method and break things.
-
# For instance, +attributes+ and +connection+ would be bad choices for association names.
-
#
-
# == Auto-generated methods
-
#
-
# === Singular associations (one-to-one)
-
# | | belongs_to |
-
# generated methods | belongs_to | :polymorphic | has_one
-
# ----------------------------------+------------+--------------+---------
-
# other | X | X | X
-
# other=(other) | X | X | X
-
# build_other(attributes={}) | X | | X
-
# create_other(attributes={}) | X | | X
-
# create_other!(attributes={}) | X | | X
-
#
-
# ===Collection associations (one-to-many / many-to-many)
-
# | | | has_many
-
# generated methods | habtm | has_many | :through
-
# ----------------------------------+-------+----------+----------
-
# others | X | X | X
-
# others=(other,other,...) | X | X | X
-
# other_ids | X | X | X
-
# other_ids=(id,id,...) | X | X | X
-
# others<< | X | X | X
-
# others.push | X | X | X
-
# others.concat | X | X | X
-
# others.build(attributes={}) | X | X | X
-
# others.create(attributes={}) | X | X | X
-
# others.create!(attributes={}) | X | X | X
-
# others.size | X | X | X
-
# others.length | X | X | X
-
# others.count | X | X | X
-
# others.sum(args*,&block) | X | X | X
-
# others.empty? | X | X | X
-
# others.clear | X | X | X
-
# others.delete(other,other,...) | X | X | X
-
# others.delete_all | X | X | X
-
# others.destroy_all | X | X | X
-
# others.find(*args) | X | X | X
-
# others.exists? | X | X | X
-
# others.uniq | X | X | X
-
# others.reset | X | X | X
-
#
-
# == Cardinality and associations
-
#
-
# Active Record associations can be used to describe one-to-one, one-to-many and many-to-many
-
# relationships between models. Each model uses an association to describe its role in
-
# the relation. The +belongs_to+ association is always used in the model that has
-
# the foreign key.
-
#
-
# === One-to-one
-
#
-
# Use +has_one+ in the base, and +belongs_to+ in the associated model.
-
#
-
# class Employee < ActiveRecord::Base
-
# has_one :office
-
# end
-
# class Office < ActiveRecord::Base
-
# belongs_to :employee # foreign key - employee_id
-
# end
-
#
-
# === One-to-many
-
#
-
# Use +has_many+ in the base, and +belongs_to+ in the associated model.
-
#
-
# class Manager < ActiveRecord::Base
-
# has_many :employees
-
# end
-
# class Employee < ActiveRecord::Base
-
# belongs_to :manager # foreign key - manager_id
-
# end
-
#
-
# === Many-to-many
-
#
-
# There are two ways to build a many-to-many relationship.
-
#
-
# The first way uses a +has_many+ association with the <tt>:through</tt> option and a join model, so
-
# there are two stages of associations.
-
#
-
# class Assignment < ActiveRecord::Base
-
# belongs_to :programmer # foreign key - programmer_id
-
# belongs_to :project # foreign key - project_id
-
# end
-
# class Programmer < ActiveRecord::Base
-
# has_many :assignments
-
# has_many :projects, :through => :assignments
-
# end
-
# class Project < ActiveRecord::Base
-
# has_many :assignments
-
# has_many :programmers, :through => :assignments
-
# end
-
#
-
# For the second way, use +has_and_belongs_to_many+ in both models. This requires a join table
-
# that has no corresponding model or primary key.
-
#
-
# class Programmer < ActiveRecord::Base
-
# has_and_belongs_to_many :projects # foreign keys in the join table
-
# end
-
# class Project < ActiveRecord::Base
-
# has_and_belongs_to_many :programmers # foreign keys in the join table
-
# end
-
#
-
# Choosing which way to build a many-to-many relationship is not always simple.
-
# If you need to work with the relationship model as its own entity,
-
# use <tt>has_many :through</tt>. Use +has_and_belongs_to_many+ when working with legacy schemas or when
-
# you never work directly with the relationship itself.
-
#
-
# == Is it a +belongs_to+ or +has_one+ association?
-
#
-
# Both express a 1-1 relationship. The difference is mostly where to place the foreign
-
# key, which goes on the table for the class declaring the +belongs_to+ relationship.
-
#
-
# class User < ActiveRecord::Base
-
# # I reference an account.
-
# belongs_to :account
-
# end
-
#
-
# class Account < ActiveRecord::Base
-
# # One user references me.
-
# has_one :user
-
# end
-
#
-
# The tables for these classes could look something like:
-
#
-
# CREATE TABLE users (
-
# id int(11) NOT NULL auto_increment,
-
# account_id int(11) default NULL,
-
# name varchar default NULL,
-
# PRIMARY KEY (id)
-
# )
-
#
-
# CREATE TABLE accounts (
-
# id int(11) NOT NULL auto_increment,
-
# name varchar default NULL,
-
# PRIMARY KEY (id)
-
# )
-
#
-
# == Unsaved objects and associations
-
#
-
# You can manipulate objects and associations before they are saved to the database, but
-
# there is some special behavior you should be aware of, mostly involving the saving of
-
# associated objects.
-
#
-
# You can set the :autosave option on a <tt>has_one</tt>, <tt>belongs_to</tt>,
-
# <tt>has_many</tt>, or <tt>has_and_belongs_to_many</tt> association. Setting it
-
# to +true+ will _always_ save the members, whereas setting it to +false+ will
-
# _never_ save the members. More details about :autosave option is available at
-
# autosave_association.rb .
-
#
-
# === One-to-one associations
-
#
-
# * Assigning an object to a +has_one+ association automatically saves that object and
-
# the object being replaced (if there is one), in order to update their foreign
-
# keys - except if the parent object is unsaved (<tt>new_record? == true</tt>).
-
# * If either of these saves fail (due to one of the objects being invalid), an
-
# <tt>ActiveRecord::RecordNotSaved</tt> exception is raised and the assignment is
-
# cancelled.
-
# * If you wish to assign an object to a +has_one+ association without saving it,
-
# use the <tt>build_association</tt> method (documented below). The object being
-
# replaced will still be saved to update its foreign key.
-
# * Assigning an object to a +belongs_to+ association does not save the object, since
-
# the foreign key field belongs on the parent. It does not save the parent either.
-
#
-
# === Collections
-
#
-
# * Adding an object to a collection (+has_many+ or +has_and_belongs_to_many+) automatically
-
# saves that object, except if the parent object (the owner of the collection) is not yet
-
# stored in the database.
-
# * If saving any of the objects being added to a collection (via <tt>push</tt> or similar)
-
# fails, then <tt>push</tt> returns +false+.
-
# * If saving fails while replacing the collection (via <tt>association=</tt>), an
-
# <tt>ActiveRecord::RecordNotSaved</tt> exception is raised and the assignment is
-
# cancelled.
-
# * You can add an object to a collection without automatically saving it by using the
-
# <tt>collection.build</tt> method (documented below).
-
# * All unsaved (<tt>new_record? == true</tt>) members of the collection are automatically
-
# saved when the parent is saved.
-
#
-
# === Association callbacks
-
#
-
# Similar to the normal callbacks that hook into the life cycle of an Active Record object,
-
# you can also define callbacks that get triggered when you add an object to or remove an
-
# object from an association collection.
-
#
-
# class Project
-
# has_and_belongs_to_many :developers, :after_add => :evaluate_velocity
-
#
-
# def evaluate_velocity(developer)
-
# ...
-
# end
-
# end
-
#
-
# It's possible to stack callbacks by passing them as an array. Example:
-
#
-
# class Project
-
# has_and_belongs_to_many :developers,
-
# :after_add => [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}]
-
# end
-
#
-
# Possible callbacks are: +before_add+, +after_add+, +before_remove+ and +after_remove+.
-
#
-
# Should any of the +before_add+ callbacks throw an exception, the object does not get
-
# added to the collection. Same with the +before_remove+ callbacks; if an exception is
-
# thrown the object doesn't get removed.
-
#
-
# === Association extensions
-
#
-
# The proxy objects that control the access to associations can be extended through anonymous
-
# modules. This is especially beneficial for adding new finders, creators, and other
-
# factory-type methods that are only used as part of this association.
-
#
-
# class Account < ActiveRecord::Base
-
# has_many :people do
-
# def find_or_create_by_name(name)
-
# first_name, last_name = name.split(" ", 2)
-
# find_or_create_by_first_name_and_last_name(first_name, last_name)
-
# end
-
# end
-
# end
-
#
-
# person = Account.first.people.find_or_create_by_name("David Heinemeier Hansson")
-
# person.first_name # => "David"
-
# person.last_name # => "Heinemeier Hansson"
-
#
-
# If you need to share the same extensions between many associations, you can use a named
-
# extension module.
-
#
-
# module FindOrCreateByNameExtension
-
# def find_or_create_by_name(name)
-
# first_name, last_name = name.split(" ", 2)
-
# find_or_create_by_first_name_and_last_name(first_name, last_name)
-
# end
-
# end
-
#
-
# class Account < ActiveRecord::Base
-
# has_many :people, :extend => FindOrCreateByNameExtension
-
# end
-
#
-
# class Company < ActiveRecord::Base
-
# has_many :people, :extend => FindOrCreateByNameExtension
-
# end
-
#
-
# If you need to use multiple named extension modules, you can specify an array of modules
-
# with the <tt>:extend</tt> option.
-
# In the case of name conflicts between methods in the modules, methods in modules later
-
# in the array supercede those earlier in the array.
-
#
-
# class Account < ActiveRecord::Base
-
# has_many :people, :extend => [FindOrCreateByNameExtension, FindRecentExtension]
-
# end
-
#
-
# Some extensions can only be made to work with knowledge of the association's internals.
-
# Extensions can access relevant state using the following methods (where +items+ is the
-
# name of the association):
-
#
-
# * <tt>record.association(:items).owner</tt> - Returns the object the association is part of.
-
# * <tt>record.association(:items).reflection</tt> - Returns the reflection object that describes the association.
-
# * <tt>record.association(:items).target</tt> - Returns the associated object for +belongs_to+ and +has_one+, or
-
# the collection of associated objects for +has_many+ and +has_and_belongs_to_many+.
-
#
-
# However, inside the actual extension code, you will not have access to the <tt>record</tt> as
-
# above. In this case, you can access <tt>proxy_association</tt>. For example,
-
# <tt>record.association(:items)</tt> and <tt>record.items.proxy_association</tt> will return
-
# the same object, allowing you to make calls like <tt>proxy_association.owner</tt> inside
-
# association extensions.
-
#
-
# === Association Join Models
-
#
-
# Has Many associations can be configured with the <tt>:through</tt> option to use an
-
# explicit join model to retrieve the data. This operates similarly to a
-
# +has_and_belongs_to_many+ association. The advantage is that you're able to add validations,
-
# callbacks, and extra attributes on the join model. Consider the following schema:
-
#
-
# class Author < ActiveRecord::Base
-
# has_many :authorships
-
# has_many :books, :through => :authorships
-
# end
-
#
-
# class Authorship < ActiveRecord::Base
-
# belongs_to :author
-
# belongs_to :book
-
# end
-
#
-
# @author = Author.first
-
# @author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to
-
# @author.books # selects all books by using the Authorship join model
-
#
-
# You can also go through a +has_many+ association on the join model:
-
#
-
# class Firm < ActiveRecord::Base
-
# has_many :clients
-
# has_many :invoices, :through => :clients
-
# end
-
#
-
# class Client < ActiveRecord::Base
-
# belongs_to :firm
-
# has_many :invoices
-
# end
-
#
-
# class Invoice < ActiveRecord::Base
-
# belongs_to :client
-
# end
-
#
-
# @firm = Firm.first
-
# @firm.clients.collect { |c| c.invoices }.flatten # select all invoices for all clients of the firm
-
# @firm.invoices # selects all invoices by going through the Client join model
-
#
-
# Similarly you can go through a +has_one+ association on the join model:
-
#
-
# class Group < ActiveRecord::Base
-
# has_many :users
-
# has_many :avatars, :through => :users
-
# end
-
#
-
# class User < ActiveRecord::Base
-
# belongs_to :group
-
# has_one :avatar
-
# end
-
#
-
# class Avatar < ActiveRecord::Base
-
# belongs_to :user
-
# end
-
#
-
# @group = Group.first
-
# @group.users.collect { |u| u.avatar }.flatten # select all avatars for all users in the group
-
# @group.avatars # selects all avatars by going through the User join model.
-
#
-
# An important caveat with going through +has_one+ or +has_many+ associations on the
-
# join model is that these associations are *read-only*. For example, the following
-
# would not work following the previous example:
-
#
-
# @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around
-
# @group.avatars.delete(@group.avatars.last) # so would this
-
#
-
# If you are using a +belongs_to+ on the join model, it is a good idea to set the
-
# <tt>:inverse_of</tt> option on the +belongs_to+, which will mean that the following example
-
# works correctly (where <tt>tags</tt> is a +has_many+ <tt>:through</tt> association):
-
#
-
# @post = Post.first
-
# @tag = @post.tags.build :name => "ruby"
-
# @tag.save
-
#
-
# The last line ought to save the through record (a <tt>Taggable</tt>). This will only work if the
-
# <tt>:inverse_of</tt> is set:
-
#
-
# class Taggable < ActiveRecord::Base
-
# belongs_to :post
-
# belongs_to :tag, :inverse_of => :taggings
-
# end
-
#
-
# === Nested Associations
-
#
-
# You can actually specify *any* association with the <tt>:through</tt> option, including an
-
# association which has a <tt>:through</tt> option itself. For example:
-
#
-
# class Author < ActiveRecord::Base
-
# has_many :posts
-
# has_many :comments, :through => :posts
-
# has_many :commenters, :through => :comments
-
# end
-
#
-
# class Post < ActiveRecord::Base
-
# has_many :comments
-
# end
-
#
-
# class Comment < ActiveRecord::Base
-
# belongs_to :commenter
-
# end
-
#
-
# @author = Author.first
-
# @author.commenters # => People who commented on posts written by the author
-
#
-
# An equivalent way of setting up this association this would be:
-
#
-
# class Author < ActiveRecord::Base
-
# has_many :posts
-
# has_many :commenters, :through => :posts
-
# end
-
#
-
# class Post < ActiveRecord::Base
-
# has_many :comments
-
# has_many :commenters, :through => :comments
-
# end
-
#
-
# class Comment < ActiveRecord::Base
-
# belongs_to :commenter
-
# end
-
#
-
# When using nested association, you will not be able to modify the association because there
-
# is not enough information to know what modification to make. For example, if you tried to
-
# add a <tt>Commenter</tt> in the example above, there would be no way to tell how to set up the
-
# intermediate <tt>Post</tt> and <tt>Comment</tt> objects.
-
#
-
# === Polymorphic Associations
-
#
-
# Polymorphic associations on models are not restricted on what types of models they
-
# can be associated with. Rather, they specify an interface that a +has_many+ association
-
# must adhere to.
-
#
-
# class Asset < ActiveRecord::Base
-
# belongs_to :attachable, :polymorphic => true
-
# end
-
#
-
# class Post < ActiveRecord::Base
-
# has_many :assets, :as => :attachable # The :as option specifies the polymorphic interface to use.
-
# end
-
#
-
# @asset.attachable = @post
-
#
-
# This works by using a type column in addition to a foreign key to specify the associated
-
# record. In the Asset example, you'd need an +attachable_id+ integer column and an
-
# +attachable_type+ string column.
-
#
-
# Using polymorphic associations in combination with single table inheritance (STI) is
-
# a little tricky. In order for the associations to work as expected, ensure that you
-
# store the base model for the STI models in the type column of the polymorphic
-
# association. To continue with the asset example above, suppose there are guest posts
-
# and member posts that use the posts table for STI. In this case, there must be a +type+
-
# column in the posts table.
-
#
-
# class Asset < ActiveRecord::Base
-
# belongs_to :attachable, :polymorphic => true
-
#
-
# def attachable_type=(sType)
-
# super(sType.to_s.classify.constantize.base_class.to_s)
-
# end
-
# end
-
#
-
# class Post < ActiveRecord::Base
-
# # because we store "Post" in attachable_type now :dependent => :destroy will work
-
# has_many :assets, :as => :attachable, :dependent => :destroy
-
# end
-
#
-
# class GuestPost < Post
-
# end
-
#
-
# class MemberPost < Post
-
# end
-
#
-
# == Caching
-
#
-
# All of the methods are built on a simple caching principle that will keep the result
-
# of the last query around unless specifically instructed not to. The cache is even
-
# shared across methods to make it even cheaper to use the macro-added methods without
-
# worrying too much about performance at the first go.
-
#
-
# project.milestones # fetches milestones from the database
-
# project.milestones.size # uses the milestone cache
-
# project.milestones.empty? # uses the milestone cache
-
# project.milestones(true).size # fetches milestones from the database
-
# project.milestones # uses the milestone cache
-
#
-
# == Eager loading of associations
-
#
-
# Eager loading is a way to find objects of a certain class and a number of named associations.
-
# This is one of the easiest ways of to prevent the dreaded 1+N problem in which fetching 100
-
# posts that each need to display their author triggers 101 database queries. Through the
-
# use of eager loading, the 101 queries can be reduced to 2.
-
#
-
# class Post < ActiveRecord::Base
-
# belongs_to :author
-
# has_many :comments
-
# end
-
#
-
# Consider the following loop using the class above:
-
#
-
# for post in Post.all
-
# puts "Post: " + post.title
-
# puts "Written by: " + post.author.name
-
# puts "Last comment on: " + post.comments.first.created_on
-
# end
-
#
-
# To iterate over these one hundred posts, we'll generate 201 database queries. Let's
-
# first just optimize it for retrieving the author:
-
#
-
# for post in Post.find(:all, :include => :author)
-
#
-
# This references the name of the +belongs_to+ association that also used the <tt>:author</tt>
-
# symbol. After loading the posts, find will collect the +author_id+ from each one and load
-
# all the referenced authors with one query. Doing so will cut down the number of queries
-
# from 201 to 102.
-
#
-
# We can improve upon the situation further by referencing both associations in the finder with:
-
#
-
# for post in Post.find(:all, :include => [ :author, :comments ])
-
#
-
# This will load all comments with a single query. This reduces the total number of queries
-
# to 3. More generally the number of queries will be 1 plus the number of associations
-
# named (except if some of the associations are polymorphic +belongs_to+ - see below).
-
#
-
# To include a deep hierarchy of associations, use a hash:
-
#
-
# for post in Post.find(:all, :include => [ :author, { :comments => { :author => :gravatar } } ])
-
#
-
# That'll grab not only all the comments but all their authors and gravatar pictures.
-
# You can mix and match symbols, arrays and hashes in any combination to describe the
-
# associations you want to load.
-
#
-
# All of this power shouldn't fool you into thinking that you can pull out huge amounts
-
# of data with no performance penalty just because you've reduced the number of queries.
-
# The database still needs to send all the data to Active Record and it still needs to
-
# be processed. So it's no catch-all for performance problems, but it's a great way to
-
# cut down on the number of queries in a situation as the one described above.
-
#
-
# Since only one table is loaded at a time, conditions or orders cannot reference tables
-
# other than the main one. If this is the case Active Record falls back to the previously
-
# used LEFT OUTER JOIN based strategy. For example
-
#
-
# Post.includes([:author, :comments]).where(['comments.approved = ?', true]).all
-
#
-
# This will result in a single SQL query with joins along the lines of:
-
# <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and
-
# <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. Note that using conditions
-
# like this can have unintended consequences.
-
# In the above example posts with no approved comments are not returned at all, because
-
# the conditions apply to the SQL statement as a whole and not just to the association.
-
# You must disambiguate column references for this fallback to happen, for example
-
# <tt>:order => "author.name DESC"</tt> will work but <tt>:order => "name DESC"</tt> will not.
-
#
-
# If you do want eager load only some members of an association it is usually more natural
-
# to <tt>:include</tt> an association which has conditions defined on it:
-
#
-
# class Post < ActiveRecord::Base
-
# has_many :approved_comments, :class_name => 'Comment', :conditions => ['approved = ?', true]
-
# end
-
#
-
# Post.find(:all, :include => :approved_comments)
-
#
-
# This will load posts and eager load the +approved_comments+ association, which contains
-
# only those comments that have been approved.
-
#
-
# If you eager load an association with a specified <tt>:limit</tt> option, it will be ignored,
-
# returning all the associated objects:
-
#
-
# class Picture < ActiveRecord::Base
-
# has_many :most_recent_comments, :class_name => 'Comment', :order => 'id DESC', :limit => 10
-
# end
-
#
-
# Picture.first(:include => :most_recent_comments).most_recent_comments # => returns all associated comments.
-
#
-
# When eager loaded, conditions are interpolated in the context of the model class, not
-
# the model instance. Conditions are lazily interpolated before the actual model exists.
-
#
-
# Eager loading is supported with polymorphic associations.
-
#
-
# class Address < ActiveRecord::Base
-
# belongs_to :addressable, :polymorphic => true
-
# end
-
#
-
# A call that tries to eager load the addressable model
-
#
-
# Address.find(:all, :include => :addressable)
-
#
-
# This will execute one query to load the addresses and load the addressables with one
-
# query per addressable type.
-
# For example if all the addressables are either of class Person or Company then a total
-
# of 3 queries will be executed. The list of addressable types to load is determined on
-
# the back of the addresses loaded. This is not supported if Active Record has to fallback
-
# to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError.
-
# The reason is that the parent model's type is a column value so its corresponding table
-
# name cannot be put in the +FROM+/+JOIN+ clauses of that query.
-
#
-
# == Table Aliasing
-
#
-
# Active Record uses table aliasing in the case that a table is referenced multiple times
-
# in a join. If a table is referenced only once, the standard table name is used. The
-
# second time, the table is aliased as <tt>#{reflection_name}_#{parent_table_name}</tt>.
-
# Indexes are appended for any more successive uses of the table name.
-
#
-
# Post.find :all, :joins => :comments
-
# # => SELECT ... FROM posts INNER JOIN comments ON ...
-
# Post.find :all, :joins => :special_comments # STI
-
# # => SELECT ... FROM posts INNER JOIN comments ON ... AND comments.type = 'SpecialComment'
-
# Post.find :all, :joins => [:comments, :special_comments] # special_comments is the reflection name, posts is the parent table name
-
# # => SELECT ... FROM posts INNER JOIN comments ON ... INNER JOIN comments special_comments_posts
-
#
-
# Acts as tree example:
-
#
-
# TreeMixin.find :all, :joins => :children
-
# # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
-
# TreeMixin.find :all, :joins => {:children => :parent}
-
# # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
-
# INNER JOIN parents_mixins ...
-
# TreeMixin.find :all, :joins => {:children => {:parent => :children}}
-
# # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
-
# INNER JOIN parents_mixins ...
-
# INNER JOIN mixins childrens_mixins_2
-
#
-
# Has and Belongs to Many join tables use the same idea, but add a <tt>_join</tt> suffix:
-
#
-
# Post.find :all, :joins => :categories
-
# # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
-
# Post.find :all, :joins => {:categories => :posts}
-
# # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
-
# INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
-
# Post.find :all, :joins => {:categories => {:posts => :categories}}
-
# # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
-
# INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
-
# INNER JOIN categories_posts categories_posts_join INNER JOIN categories categories_posts_2
-
#
-
# If you wish to specify your own custom joins using a <tt>:joins</tt> option, those table
-
# names will take precedence over the eager associations:
-
#
-
# Post.find :all, :joins => :comments, :joins => "inner join comments ..."
-
# # => SELECT ... FROM posts INNER JOIN comments_posts ON ... INNER JOIN comments ...
-
# Post.find :all, :joins => [:comments, :special_comments], :joins => "inner join comments ..."
-
# # => SELECT ... FROM posts INNER JOIN comments comments_posts ON ...
-
# INNER JOIN comments special_comments_posts ...
-
# INNER JOIN comments ...
-
#
-
# Table aliases are automatically truncated according to the maximum length of table identifiers
-
# according to the specific database.
-
#
-
# == Modules
-
#
-
# By default, associations will look for objects within the current module scope. Consider:
-
#
-
# module MyApplication
-
# module Business
-
# class Firm < ActiveRecord::Base
-
# has_many :clients
-
# end
-
#
-
# class Client < ActiveRecord::Base; end
-
# end
-
# end
-
#
-
# When <tt>Firm#clients</tt> is called, it will in turn call
-
# <tt>MyApplication::Business::Client.find_all_by_firm_id(firm.id)</tt>.
-
# If you want to associate with a class in another module scope, this can be done by
-
# specifying the complete class name.
-
#
-
# module MyApplication
-
# module Business
-
# class Firm < ActiveRecord::Base; end
-
# end
-
#
-
# module Billing
-
# class Account < ActiveRecord::Base
-
# belongs_to :firm, :class_name => "MyApplication::Business::Firm"
-
# end
-
# end
-
# end
-
#
-
# == Bi-directional associations
-
#
-
# When you specify an association there is usually an association on the associated model
-
# that specifies the same relationship in reverse. For example, with the following models:
-
#
-
# class Dungeon < ActiveRecord::Base
-
# has_many :traps
-
# has_one :evil_wizard
-
# end
-
#
-
# class Trap < ActiveRecord::Base
-
# belongs_to :dungeon
-
# end
-
#
-
# class EvilWizard < ActiveRecord::Base
-
# belongs_to :dungeon
-
# end
-
#
-
# The +traps+ association on +Dungeon+ and the +dungeon+ association on +Trap+ are
-
# the inverse of each other and the inverse of the +dungeon+ association on +EvilWizard+
-
# is the +evil_wizard+ association on +Dungeon+ (and vice-versa). By default,
-
# Active Record doesn't know anything about these inverse relationships and so no object
-
# loading optimisation is possible. For example:
-
#
-
# d = Dungeon.first
-
# t = d.traps.first
-
# d.level == t.dungeon.level # => true
-
# d.level = 10
-
# d.level == t.dungeon.level # => false
-
#
-
# The +Dungeon+ instances +d+ and <tt>t.dungeon</tt> in the above example refer to
-
# the same object data from the database, but are actually different in-memory copies
-
# of that data. Specifying the <tt>:inverse_of</tt> option on associations lets you tell
-
# Active Record about inverse relationships and it will optimise object loading. For
-
# example, if we changed our model definitions to:
-
#
-
# class Dungeon < ActiveRecord::Base
-
# has_many :traps, :inverse_of => :dungeon
-
# has_one :evil_wizard, :inverse_of => :dungeon
-
# end
-
#
-
# class Trap < ActiveRecord::Base
-
# belongs_to :dungeon, :inverse_of => :traps
-
# end
-
#
-
# class EvilWizard < ActiveRecord::Base
-
# belongs_to :dungeon, :inverse_of => :evil_wizard
-
# end
-
#
-
# Then, from our code snippet above, +d+ and <tt>t.dungeon</tt> are actually the same
-
# in-memory instance and our final <tt>d.level == t.dungeon.level</tt> will return +true+.
-
#
-
# There are limitations to <tt>:inverse_of</tt> support:
-
#
-
# * does not work with <tt>:through</tt> associations.
-
# * does not work with <tt>:polymorphic</tt> associations.
-
# * for +belongs_to+ associations +has_many+ inverse associations are ignored.
-
#
-
# == Deleting from associations
-
#
-
# === Dependent associations
-
#
-
# +has_many+, +has_one+ and +belongs_to+ associations support the <tt>:dependent</tt> option.
-
# This allows you to specify that associated records should be deleted when the owner is
-
# deleted.
-
#
-
# For example:
-
#
-
# class Author
-
# has_many :posts, :dependent => :destroy
-
# end
-
# Author.find(1).destroy # => Will destroy all of the author's posts, too
-
#
-
# The <tt>:dependent</tt> option can have different values which specify how the deletion
-
# is done. For more information, see the documentation for this option on the different
-
# specific association types.
-
#
-
# === Delete or destroy?
-
#
-
# +has_many+ and +has_and_belongs_to_many+ associations have the methods <tt>destroy</tt>,
-
# <tt>delete</tt>, <tt>destroy_all</tt> and <tt>delete_all</tt>.
-
#
-
# For +has_and_belongs_to_many+, <tt>delete</tt> and <tt>destroy</tt> are the same: they
-
# cause the records in the join table to be removed.
-
#
-
# For +has_many+, <tt>destroy</tt> will always call the <tt>destroy</tt> method of the
-
# record(s) being removed so that callbacks are run. However <tt>delete</tt> will either
-
# do the deletion according to the strategy specified by the <tt>:dependent</tt> option, or
-
# if no <tt>:dependent</tt> option is given, then it will follow the default strategy.
-
# The default strategy is <tt>:nullify</tt> (set the foreign keys to <tt>nil</tt>), except for
-
# +has_many+ <tt>:through</tt>, where the default strategy is <tt>delete_all</tt> (delete
-
# the join records, without running their callbacks).
-
#
-
# There is also a <tt>clear</tt> method which is the same as <tt>delete_all</tt>, except that
-
# it returns the association rather than the records which have been deleted.
-
#
-
# === What gets deleted?
-
#
-
# There is a potential pitfall here: +has_and_belongs_to_many+ and +has_many+ <tt>:through</tt>
-
# associations have records in join tables, as well as the associated records. So when we
-
# call one of these deletion methods, what exactly should be deleted?
-
#
-
# The answer is that it is assumed that deletion on an association is about removing the
-
# <i>link</i> between the owner and the associated object(s), rather than necessarily the
-
# associated objects themselves. So with +has_and_belongs_to_many+ and +has_many+
-
# <tt>:through</tt>, the join records will be deleted, but the associated records won't.
-
#
-
# This makes sense if you think about it: if you were to call <tt>post.tags.delete(Tag.find_by_name('food'))</tt>
-
# you would want the 'food' tag to be unlinked from the post, rather than for the tag itself
-
# to be removed from the database.
-
#
-
# However, there are examples where this strategy doesn't make sense. For example, suppose
-
# a person has many projects, and each project has many tasks. If we deleted one of a person's
-
# tasks, we would probably not want the project to be deleted. In this scenario, the delete method
-
# won't actually work: it can only be used if the association on the join model is a
-
# +belongs_to+. In other situations you are expected to perform operations directly on
-
# either the associated records or the <tt>:through</tt> association.
-
#
-
# With a regular +has_many+ there is no distinction between the "associated records"
-
# and the "link", so there is only one choice for what gets deleted.
-
#
-
# With +has_and_belongs_to_many+ and +has_many+ <tt>:through</tt>, if you want to delete the
-
# associated records themselves, you can always do something along the lines of
-
# <tt>person.tasks.each(&:destroy)</tt>.
-
#
-
# == Type safety with <tt>ActiveRecord::AssociationTypeMismatch</tt>
-
#
-
# If you attempt to assign an object to an association that doesn't match the inferred
-
# or specified <tt>:class_name</tt>, you'll get an <tt>ActiveRecord::AssociationTypeMismatch</tt>.
-
#
-
# == Options
-
#
-
# All of the association macros can be specialized through options. This makes cases
-
# more complex than the simple and guessable ones possible.
-
1
module ClassMethods
-
# Specifies a one-to-many association. The following methods for retrieval and query of
-
# collections of associated objects will be added:
-
#
-
# [collection(force_reload = false)]
-
# Returns an array of all the associated objects.
-
# An empty array is returned if none are found.
-
# [collection<<(object, ...)]
-
# Adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
-
# Note that this operation instantly fires update sql without waiting for the save or update call on the
-
# parent object.
-
# [collection.delete(object, ...)]
-
# Removes one or more objects from the collection by setting their foreign keys to +NULL+.
-
# Objects will be in addition destroyed if they're associated with <tt>:dependent => :destroy</tt>,
-
# and deleted if they're associated with <tt>:dependent => :delete_all</tt>.
-
#
-
# If the <tt>:through</tt> option is used, then the join records are deleted (rather than
-
# nullified) by default, but you can specify <tt>:dependent => :destroy</tt> or
-
# <tt>:dependent => :nullify</tt> to override this.
-
# [collection=objects]
-
# Replaces the collections content by deleting and adding objects as appropriate. If the <tt>:through</tt>
-
# option is true callbacks in the join models are triggered except destroy callbacks, since deletion is
-
# direct.
-
# [collection_singular_ids]
-
# Returns an array of the associated objects' ids
-
# [collection_singular_ids=ids]
-
# Replace the collection with the objects identified by the primary keys in +ids+. This
-
# method loads the models and calls <tt>collection=</tt>. See above.
-
# [collection.clear]
-
# Removes every object from the collection. This destroys the associated objects if they
-
# are associated with <tt>:dependent => :destroy</tt>, deletes them directly from the
-
# database if <tt>:dependent => :delete_all</tt>, otherwise sets their foreign keys to +NULL+.
-
# If the <tt>:through</tt> option is true no destroy callbacks are invoked on the join models.
-
# Join models are directly deleted.
-
# [collection.empty?]
-
# Returns +true+ if there are no associated objects.
-
# [collection.size]
-
# Returns the number of associated objects.
-
# [collection.find(...)]
-
# Finds an associated object according to the same rules as ActiveRecord::Base.find.
-
# [collection.exists?(...)]
-
# Checks whether an associated object with the given conditions exists.
-
# Uses the same rules as ActiveRecord::Base.exists?.
-
# [collection.build(attributes = {}, ...)]
-
# Returns one or more new objects of the collection type that have been instantiated
-
# with +attributes+ and linked to this object through a foreign key, but have not yet
-
# been saved.
-
# [collection.create(attributes = {})]
-
# Returns a new object of the collection type that has been instantiated
-
# with +attributes+, linked to this object through a foreign key, and that has already
-
# been saved (if it passed the validation). *Note*: This only works if the base model
-
# already exists in the DB, not if it is a new (unsaved) record!
-
#
-
# (*Note*: +collection+ is replaced with the symbol passed as the first argument, so
-
# <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.)
-
#
-
# === Example
-
#
-
# Example: A Firm class declares <tt>has_many :clients</tt>, which will add:
-
# * <tt>Firm#clients</tt> (similar to <tt>Clients.find :all, :conditions => ["firm_id = ?", id]</tt>)
-
# * <tt>Firm#clients<<</tt>
-
# * <tt>Firm#clients.delete</tt>
-
# * <tt>Firm#clients=</tt>
-
# * <tt>Firm#client_ids</tt>
-
# * <tt>Firm#client_ids=</tt>
-
# * <tt>Firm#clients.clear</tt>
-
# * <tt>Firm#clients.empty?</tt> (similar to <tt>firm.clients.size == 0</tt>)
-
# * <tt>Firm#clients.size</tt> (similar to <tt>Client.count "firm_id = #{id}"</tt>)
-
# * <tt>Firm#clients.find</tt> (similar to <tt>Client.find(id, :conditions => "firm_id = #{id}")</tt>)
-
# * <tt>Firm#clients.exists?(:name => 'ACME')</tt> (similar to <tt>Client.exists?(:name => 'ACME', :firm_id => firm.id)</tt>)
-
# * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>)
-
# * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>)
-
# The declaration can also include an options hash to specialize the behavior of the association.
-
#
-
# === Options
-
# [:class_name]
-
# Specify the class name of the association. Use it only if that name can't be inferred
-
# from the association name. So <tt>has_many :products</tt> will by default be linked
-
# to the Product class, but if the real class name is SpecialProduct, you'll have to
-
# specify it with this option.
-
# [:conditions]
-
# Specify the conditions that the associated objects must meet in order to be included as a +WHERE+
-
# SQL fragment, such as <tt>price > 5 AND name LIKE 'B%'</tt>. Record creations from
-
# the association are scoped if a hash is used.
-
# <tt>has_many :posts, :conditions => {:published => true}</tt> will create published
-
# posts with <tt>@blog.posts.create</tt> or <tt>@blog.posts.build</tt>.
-
# [:order]
-
# Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
-
# such as <tt>last_name, first_name DESC</tt>.
-
# [:foreign_key]
-
# Specify the foreign key used for the association. By default this is guessed to be the name
-
# of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_many+
-
# association will use "person_id" as the default <tt>:foreign_key</tt>.
-
# [:primary_key]
-
# Specify the method that returns the primary key used for the association. By default this is +id+.
-
# [:dependent]
-
# If set to <tt>:destroy</tt> all the associated objects are destroyed
-
# alongside this object by calling their +destroy+ method. If set to <tt>:delete_all</tt> all associated
-
# objects are deleted *without* calling their +destroy+ method. If set to <tt>:nullify</tt> all associated
-
# objects' foreign keys are set to +NULL+ *without* calling their +save+ callbacks. If set to
-
# <tt>:restrict</tt> this object cannot be deleted if it has any associated object.
-
#
-
# If using with the <tt>:through</tt> option, the association on the join model must be
-
# a +belongs_to+, and the records which get deleted are the join records, rather than
-
# the associated records.
-
#
-
# [:finder_sql]
-
# Specify a complete SQL statement to fetch the association. This is a good way to go for complex
-
# associations that depend on multiple tables. Note: When this option is used, +find_in_collection+
-
# is _not_ added.
-
# [:counter_sql]
-
# Specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
-
# specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by
-
# replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>.
-
# [:extend]
-
# Specify a named module for extending the proxy. See "Association extensions".
-
# [:include]
-
# Specify second-order associations that should be eager loaded when the collection is loaded.
-
# [:group]
-
# An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
-
# [:having]
-
# Combined with +:group+ this can be used to filter the records that a <tt>GROUP BY</tt>
-
# returns. Uses the <tt>HAVING</tt> SQL-clause.
-
# [:limit]
-
# An integer determining the limit on the number of rows that should be returned.
-
# [:offset]
-
# An integer determining the offset from where the rows should be fetched. So at 5,
-
# it would skip the first 4 rows.
-
# [:select]
-
# By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if
-
# you, for example, want to do a join but not include the joined columns. Do not forget
-
# to include the primary and foreign keys, otherwise it will raise an error.
-
# [:as]
-
# Specifies a polymorphic interface (See <tt>belongs_to</tt>).
-
# [:through]
-
# Specifies an association through which to perform the query. This can be any other type
-
# of association, including other <tt>:through</tt> associations. Options for <tt>:class_name</tt>,
-
# <tt>:primary_key</tt> and <tt>:foreign_key</tt> are ignored, as the association uses the
-
# source reflection.
-
#
-
# If the association on the join model is a +belongs_to+, the collection can be modified
-
# and the records on the <tt>:through</tt> model will be automatically created and removed
-
# as appropriate. Otherwise, the collection is read-only, so you should manipulate the
-
# <tt>:through</tt> association directly.
-
#
-
# If you are going to modify the association (rather than just read from it), then it is
-
# a good idea to set the <tt>:inverse_of</tt> option on the source association on the
-
# join model. This allows associated records to be built which will automatically create
-
# the appropriate join model records when they are saved. (See the 'Association Join Models'
-
# section above.)
-
# [:source]
-
# Specifies the source association name used by <tt>has_many :through</tt> queries.
-
# Only use it if the name cannot be inferred from the association.
-
# <tt>has_many :subscribers, :through => :subscriptions</tt> will look for either <tt>:subscribers</tt> or
-
# <tt>:subscriber</tt> on Subscription, unless a <tt>:source</tt> is given.
-
# [:source_type]
-
# Specifies type of the source association used by <tt>has_many :through</tt> queries where the source
-
# association is a polymorphic +belongs_to+.
-
# [:uniq]
-
# If true, duplicates will be omitted from the collection. Useful in conjunction with <tt>:through</tt>.
-
# [:readonly]
-
# If true, all the associated objects are readonly through the association.
-
# [:validate]
-
# If +false+, don't validate the associated objects when saving the parent object. true by default.
-
# [:autosave]
-
# If true, always save the associated objects or destroy them if marked for destruction,
-
# when saving the parent object. If false, never save or destroy the associated objects.
-
# By default, only save associated objects that are new records.
-
# [:inverse_of]
-
# Specifies the name of the <tt>belongs_to</tt> association on the associated object
-
# that is the inverse of this <tt>has_many</tt> association. Does not work in combination
-
# with <tt>:through</tt> or <tt>:as</tt> options.
-
# See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
-
#
-
# Option examples:
-
# has_many :comments, :order => "posted_on"
-
# has_many :comments, :include => :author
-
# has_many :people, :class_name => "Person", :conditions => "deleted = 0", :order => "name"
-
# has_many :tracks, :order => "position", :dependent => :destroy
-
# has_many :comments, :dependent => :nullify
-
# has_many :tags, :as => :taggable
-
# has_many :reports, :readonly => true
-
# has_many :subscribers, :through => :subscriptions, :source => :user
-
# has_many :subscribers, :class_name => "Person", :finder_sql =>
-
# 'SELECT DISTINCT people.* ' +
-
# 'FROM people p, post_subscriptions ps ' +
-
# 'WHERE ps.post_id = #{id} AND ps.person_id = p.id ' +
-
# 'ORDER BY p.first_name'
-
1
def has_many(name, options = {}, &extension)
-
Builder::HasMany.build(self, name, options, &extension)
-
end
-
-
# Specifies a one-to-one association with another class. This method should only be used
-
# if the other class contains the foreign key. If the current class contains the foreign key,
-
# then you should use +belongs_to+ instead. See also ActiveRecord::Associations::ClassMethods's overview
-
# on when to use has_one and when to use belongs_to.
-
#
-
# The following methods for retrieval and query of a single associated object will be added:
-
#
-
# [association(force_reload = false)]
-
# Returns the associated object. +nil+ is returned if none is found.
-
# [association=(associate)]
-
# Assigns the associate object, extracts the primary key, sets it as the foreign key,
-
# and saves the associate object.
-
# [build_association(attributes = {})]
-
# Returns a new object of the associated type that has been instantiated
-
# with +attributes+ and linked to this object through a foreign key, but has not
-
# yet been saved.
-
# [create_association(attributes = {})]
-
# Returns a new object of the associated type that has been instantiated
-
# with +attributes+, linked to this object through a foreign key, and that
-
# has already been saved (if it passed the validation).
-
# [create_association!(attributes = {})]
-
# Does the same as <tt>create_association</tt>, but raises <tt>ActiveRecord::RecordInvalid</tt>
-
# if the record is invalid.
-
#
-
# (+association+ is replaced with the symbol passed as the first argument, so
-
# <tt>has_one :manager</tt> would add among others <tt>manager.nil?</tt>.)
-
#
-
# === Example
-
#
-
# An Account class declares <tt>has_one :beneficiary</tt>, which will add:
-
# * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.first(:conditions => "account_id = #{id}")</tt>)
-
# * <tt>Account#beneficiary=(beneficiary)</tt> (similar to <tt>beneficiary.account_id = account.id; beneficiary.save</tt>)
-
# * <tt>Account#build_beneficiary</tt> (similar to <tt>Beneficiary.new("account_id" => id)</tt>)
-
# * <tt>Account#create_beneficiary</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save; b</tt>)
-
# * <tt>Account#create_beneficiary!</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save!; b</tt>)
-
#
-
# === Options
-
#
-
# The declaration can also include an options hash to specialize the behavior of the association.
-
#
-
# Options are:
-
# [:class_name]
-
# Specify the class name of the association. Use it only if that name can't be inferred
-
# from the association name. So <tt>has_one :manager</tt> will by default be linked to the Manager class, but
-
# if the real class name is Person, you'll have to specify it with this option.
-
# [:conditions]
-
# Specify the conditions that the associated object must meet in order to be included as a +WHERE+
-
# SQL fragment, such as <tt>rank = 5</tt>. Record creation from the association is scoped if a hash
-
# is used. <tt>has_one :account, :conditions => {:enabled => true}</tt> will create
-
# an enabled account with <tt>@company.create_account</tt> or <tt>@company.build_account</tt>.
-
# [:order]
-
# Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
-
# such as <tt>last_name, first_name DESC</tt>.
-
# [:dependent]
-
# If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
-
# <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method.
-
# If set to <tt>:nullify</tt>, the associated object's foreign key is set to +NULL+.
-
# Also, association is assigned.
-
# [:foreign_key]
-
# Specify the foreign key used for the association. By default this is guessed to be the name
-
# of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_one+ association
-
# will use "person_id" as the default <tt>:foreign_key</tt>.
-
# [:primary_key]
-
# Specify the method that returns the primary key used for the association. By default this is +id+.
-
# [:include]
-
# Specify second-order associations that should be eager loaded when this object is loaded.
-
# [:as]
-
# Specifies a polymorphic interface (See <tt>belongs_to</tt>).
-
# [:select]
-
# By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example,
-
# you want to do a join but not include the joined columns. Do not forget to include the
-
# primary and foreign keys, otherwise it will raise an error.
-
# [:through]
-
# Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt>,
-
# <tt>:primary_key</tt>, and <tt>:foreign_key</tt> are ignored, as the association uses the
-
# source reflection. You can only use a <tt>:through</tt> query through a <tt>has_one</tt>
-
# or <tt>belongs_to</tt> association on the join model.
-
# [:source]
-
# Specifies the source association name used by <tt>has_one :through</tt> queries.
-
# Only use it if the name cannot be inferred from the association.
-
# <tt>has_one :favorite, :through => :favorites</tt> will look for a
-
# <tt>:favorite</tt> on Favorite, unless a <tt>:source</tt> is given.
-
# [:source_type]
-
# Specifies type of the source association used by <tt>has_one :through</tt> queries where the source
-
# association is a polymorphic +belongs_to+.
-
# [:readonly]
-
# If true, the associated object is readonly through the association.
-
# [:validate]
-
# If +false+, don't validate the associated object when saving the parent object. +false+ by default.
-
# [:autosave]
-
# If true, always save the associated object or destroy it if marked for destruction,
-
# when saving the parent object. If false, never save or destroy the associated object.
-
# By default, only save the associated object if it's a new record.
-
# [:inverse_of]
-
# Specifies the name of the <tt>belongs_to</tt> association on the associated object
-
# that is the inverse of this <tt>has_one</tt> association. Does not work in combination
-
# with <tt>:through</tt> or <tt>:as</tt> options.
-
# See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
-
#
-
# Option examples:
-
# has_one :credit_card, :dependent => :destroy # destroys the associated credit card
-
# has_one :credit_card, :dependent => :nullify # updates the associated records foreign
-
# # key value to NULL rather than destroying it
-
# has_one :last_comment, :class_name => "Comment", :order => "posted_on"
-
# has_one :project_manager, :class_name => "Person", :conditions => "role = 'project_manager'"
-
# has_one :attachment, :as => :attachable
-
# has_one :boss, :readonly => :true
-
# has_one :club, :through => :membership
-
# has_one :primary_address, :through => :addressables, :conditions => ["addressable.primary = ?", true], :source => :addressable
-
1
def has_one(name, options = {})
-
3
Builder::HasOne.build(self, name, options)
-
end
-
-
# Specifies a one-to-one association with another class. This method should only be used
-
# if this class contains the foreign key. If the other class contains the foreign key,
-
# then you should use +has_one+ instead. See also ActiveRecord::Associations::ClassMethods's overview
-
# on when to use +has_one+ and when to use +belongs_to+.
-
#
-
# Methods will be added for retrieval and query for a single associated object, for which
-
# this object holds an id:
-
#
-
# [association(force_reload = false)]
-
# Returns the associated object. +nil+ is returned if none is found.
-
# [association=(associate)]
-
# Assigns the associate object, extracts the primary key, and sets it as the foreign key.
-
# [build_association(attributes = {})]
-
# Returns a new object of the associated type that has been instantiated
-
# with +attributes+ and linked to this object through a foreign key, but has not yet been saved.
-
# [create_association(attributes = {})]
-
# Returns a new object of the associated type that has been instantiated
-
# with +attributes+, linked to this object through a foreign key, and that
-
# has already been saved (if it passed the validation).
-
# [create_association!(attributes = {})]
-
# Does the same as <tt>create_association</tt>, but raises <tt>ActiveRecord::RecordInvalid</tt>
-
# if the record is invalid.
-
#
-
# (+association+ is replaced with the symbol passed as the first argument, so
-
# <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>.)
-
#
-
# === Example
-
#
-
# A Post class declares <tt>belongs_to :author</tt>, which will add:
-
# * <tt>Post#author</tt> (similar to <tt>Author.find(author_id)</tt>)
-
# * <tt>Post#author=(author)</tt> (similar to <tt>post.author_id = author.id</tt>)
-
# * <tt>Post#build_author</tt> (similar to <tt>post.author = Author.new</tt>)
-
# * <tt>Post#create_author</tt> (similar to <tt>post.author = Author.new; post.author.save; post.author</tt>)
-
# * <tt>Post#create_author!</tt> (similar to <tt>post.author = Author.new; post.author.save!; post.author</tt>)
-
# The declaration can also include an options hash to specialize the behavior of the association.
-
#
-
# === Options
-
#
-
# [:class_name]
-
# Specify the class name of the association. Use it only if that name can't be inferred
-
# from the association name. So <tt>has_one :author</tt> will by default be linked to the Author class, but
-
# if the real class name is Person, you'll have to specify it with this option.
-
# [:conditions]
-
# Specify the conditions that the associated object must meet in order to be included as a +WHERE+
-
# SQL fragment, such as <tt>authorized = 1</tt>.
-
# [:select]
-
# By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed
-
# if, for example, you want to do a join but not include the joined columns. Do not
-
# forget to include the primary and foreign keys, otherwise it will raise an error.
-
# [:foreign_key]
-
# Specify the foreign key used for the association. By default this is guessed to be the name
-
# of the association with an "_id" suffix. So a class that defines a <tt>belongs_to :person</tt>
-
# association will use "person_id" as the default <tt>:foreign_key</tt>. Similarly,
-
# <tt>belongs_to :favorite_person, :class_name => "Person"</tt> will use a foreign key
-
# of "favorite_person_id".
-
# [:foreign_type]
-
# Specify the column used to store the associated object's type, if this is a polymorphic
-
# association. By default this is guessed to be the name of the association with a "_type"
-
# suffix. So a class that defines a <tt>belongs_to :taggable, :polymorphic => true</tt>
-
# association will use "taggable_type" as the default <tt>:foreign_type</tt>.
-
# [:primary_key]
-
# Specify the method that returns the primary key of associated object used for the association.
-
# By default this is id.
-
# [:dependent]
-
# If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
-
# <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method.
-
# This option should not be specified when <tt>belongs_to</tt> is used in conjunction with
-
# a <tt>has_many</tt> relationship on another class because of the potential to leave
-
# orphaned records behind.
-
# [:counter_cache]
-
# Caches the number of belonging objects on the associate class through the use of +increment_counter+
-
# and +decrement_counter+. The counter cache is incremented when an object of this
-
# class is created and decremented when it's destroyed. This requires that a column
-
# named <tt>#{table_name}_count</tt> (such as +comments_count+ for a belonging Comment class)
-
# is used on the associate class (such as a Post class). You can also specify a custom counter
-
# cache column by providing a column name instead of a +true+/+false+ value to this
-
# option (e.g., <tt>:counter_cache => :my_custom_counter</tt>.)
-
# Note: Specifying a counter cache will add it to that model's list of readonly attributes
-
# using +attr_readonly+.
-
# [:include]
-
# Specify second-order associations that should be eager loaded when this object is loaded.
-
# [:polymorphic]
-
# Specify this association is a polymorphic association by passing +true+.
-
# Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
-
# to the +attr_readonly+ list in the associated classes (e.g. <tt>class Post; attr_readonly :comments_count; end</tt>).
-
# [:readonly]
-
# If true, the associated object is readonly through the association.
-
# [:validate]
-
# If +false+, don't validate the associated objects when saving the parent object. +false+ by default.
-
# [:autosave]
-
# If true, always save the associated object or destroy it if marked for destruction, when
-
# saving the parent object.
-
# If false, never save or destroy the associated object.
-
# By default, only save the associated object if it's a new record.
-
# [:touch]
-
# If true, the associated object will be touched (the updated_at/on attributes set to now)
-
# when this record is either saved or destroyed. If you specify a symbol, that attribute
-
# will be updated with the current time in addition to the updated_at/on attribute.
-
# [:inverse_of]
-
# Specifies the name of the <tt>has_one</tt> or <tt>has_many</tt> association on the associated
-
# object that is the inverse of this <tt>belongs_to</tt> association. Does not work in
-
# combination with the <tt>:polymorphic</tt> options.
-
# See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
-
#
-
# Option examples:
-
# belongs_to :firm, :foreign_key => "client_of"
-
# belongs_to :person, :primary_key => "name", :foreign_key => "person_name"
-
# belongs_to :author, :class_name => "Person", :foreign_key => "author_id"
-
# belongs_to :valid_coupon, :class_name => "Coupon", :foreign_key => "coupon_id",
-
# :conditions => 'discounts > #{payments_count}'
-
# belongs_to :attachable, :polymorphic => true
-
# belongs_to :project, :readonly => true
-
# belongs_to :post, :counter_cache => true
-
# belongs_to :company, :touch => true
-
# belongs_to :company, :touch => :employees_last_updated_at
-
1
def belongs_to(name, options = {})
-
2
Builder::BelongsTo.build(self, name, options)
-
end
-
-
# Specifies a many-to-many relationship with another class. This associates two classes via an
-
# intermediate join table. Unless the join table is explicitly specified as an option, it is
-
# guessed using the lexical order of the class names. So a join between Developer and Project
-
# will give the default join table name of "developers_projects" because "D" outranks "P".
-
# Note that this precedence is calculated using the <tt><</tt> operator for String. This
-
# means that if the strings are of different lengths, and the strings are equal when compared
-
# up to the shortest length, then the longer string is considered of higher
-
# lexical precedence than the shorter one. For example, one would expect the tables "paper_boxes" and "papers"
-
# to generate a join table name of "papers_paper_boxes" because of the length of the name "paper_boxes",
-
# but it in fact generates a join table name of "paper_boxes_papers". Be aware of this caveat, and use the
-
# custom <tt>:join_table</tt> option if you need to.
-
#
-
# The join table should not have a primary key or a model associated with it. You must manually generate the
-
# join table with a migration such as this:
-
#
-
# class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration
-
# def self.up
-
# create_table :developers_projects, :id => false do |t|
-
# t.integer :developer_id
-
# t.integer :project_id
-
# end
-
# end
-
#
-
# def self.down
-
# drop_table :developers_projects
-
# end
-
# end
-
#
-
# Adds the following methods for retrieval and query:
-
#
-
# [collection(force_reload = false)]
-
# Returns an array of all the associated objects.
-
# An empty array is returned if none are found.
-
# [collection<<(object, ...)]
-
# Adds one or more objects to the collection by creating associations in the join table
-
# (<tt>collection.push</tt> and <tt>collection.concat</tt> are aliases to this method).
-
# Note that this operation instantly fires update sql without waiting for the save or update call on the
-
# parent object.
-
# [collection.delete(object, ...)]
-
# Removes one or more objects from the collection by removing their associations from the join table.
-
# This does not destroy the objects.
-
# [collection=objects]
-
# Replaces the collection's content by deleting and adding objects as appropriate.
-
# [collection_singular_ids]
-
# Returns an array of the associated objects' ids.
-
# [collection_singular_ids=ids]
-
# Replace the collection by the objects identified by the primary keys in +ids+.
-
# [collection.clear]
-
# Removes every object from the collection. This does not destroy the objects.
-
# [collection.empty?]
-
# Returns +true+ if there are no associated objects.
-
# [collection.size]
-
# Returns the number of associated objects.
-
# [collection.find(id)]
-
# Finds an associated object responding to the +id+ and that
-
# meets the condition that it has to be associated with this object.
-
# Uses the same rules as ActiveRecord::Base.find.
-
# [collection.exists?(...)]
-
# Checks whether an associated object with the given conditions exists.
-
# Uses the same rules as ActiveRecord::Base.exists?.
-
# [collection.build(attributes = {})]
-
# Returns a new object of the collection type that has been instantiated
-
# with +attributes+ and linked to this object through the join table, but has not yet been saved.
-
# [collection.create(attributes = {})]
-
# Returns a new object of the collection type that has been instantiated
-
# with +attributes+, linked to this object through the join table, and that has already been
-
# saved (if it passed the validation).
-
#
-
# (+collection+ is replaced with the symbol passed as the first argument, so
-
# <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>.)
-
#
-
# === Example
-
#
-
# A Developer class declares <tt>has_and_belongs_to_many :projects</tt>, which will add:
-
# * <tt>Developer#projects</tt>
-
# * <tt>Developer#projects<<</tt>
-
# * <tt>Developer#projects.delete</tt>
-
# * <tt>Developer#projects=</tt>
-
# * <tt>Developer#project_ids</tt>
-
# * <tt>Developer#project_ids=</tt>
-
# * <tt>Developer#projects.clear</tt>
-
# * <tt>Developer#projects.empty?</tt>
-
# * <tt>Developer#projects.size</tt>
-
# * <tt>Developer#projects.find(id)</tt>
-
# * <tt>Developer#projects.exists?(...)</tt>
-
# * <tt>Developer#projects.build</tt> (similar to <tt>Project.new("project_id" => id)</tt>)
-
# * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("project_id" => id); c.save; c</tt>)
-
# The declaration may include an options hash to specialize the behavior of the association.
-
#
-
# === Options
-
#
-
# [:class_name]
-
# Specify the class name of the association. Use it only if that name can't be inferred
-
# from the association name. So <tt>has_and_belongs_to_many :projects</tt> will by default be linked to the
-
# Project class, but if the real class name is SuperProject, you'll have to specify it with this option.
-
# [:join_table]
-
# Specify the name of the join table if the default based on lexical order isn't what you want.
-
# <b>WARNING:</b> If you're overwriting the table name of either class, the +table_name+ method
-
# MUST be declared underneath any +has_and_belongs_to_many+ declaration in order to work.
-
# [:foreign_key]
-
# Specify the foreign key used for the association. By default this is guessed to be the name
-
# of this class in lower-case and "_id" suffixed. So a Person class that makes
-
# a +has_and_belongs_to_many+ association to Project will use "person_id" as the
-
# default <tt>:foreign_key</tt>.
-
# [:association_foreign_key]
-
# Specify the foreign key used for the association on the receiving side of the association.
-
# By default this is guessed to be the name of the associated class in lower-case and "_id" suffixed.
-
# So if a Person class makes a +has_and_belongs_to_many+ association to Project,
-
# the association will use "project_id" as the default <tt>:association_foreign_key</tt>.
-
# [:conditions]
-
# Specify the conditions that the associated object must meet in order to be included as a +WHERE+
-
# SQL fragment, such as <tt>authorized = 1</tt>. Record creations from the association are
-
# scoped if a hash is used.
-
# <tt>has_many :posts, :conditions => {:published => true}</tt> will create published posts with <tt>@blog.posts.create</tt>
-
# or <tt>@blog.posts.build</tt>.
-
# [:order]
-
# Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
-
# such as <tt>last_name, first_name DESC</tt>
-
# [:uniq]
-
# If true, duplicate associated objects will be ignored by accessors and query methods.
-
# [:finder_sql]
-
# Overwrite the default generated SQL statement used to fetch the association with a manual statement
-
# [:counter_sql]
-
# Specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
-
# specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by
-
# replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>.
-
# [:delete_sql]
-
# Overwrite the default generated SQL statement used to remove links between the associated
-
# classes with a manual statement.
-
# [:insert_sql]
-
# Overwrite the default generated SQL statement used to add links between the associated classes
-
# with a manual statement.
-
# [:extend]
-
# Anonymous module for extending the proxy, see "Association extensions".
-
# [:include]
-
# Specify second-order associations that should be eager loaded when the collection is loaded.
-
# [:group]
-
# An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
-
# [:having]
-
# Combined with +:group+ this can be used to filter the records that a <tt>GROUP BY</tt> returns.
-
# Uses the <tt>HAVING</tt> SQL-clause.
-
# [:limit]
-
# An integer determining the limit on the number of rows that should be returned.
-
# [:offset]
-
# An integer determining the offset from where the rows should be fetched. So at 5,
-
# it would skip the first 4 rows.
-
# [:select]
-
# By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example,
-
# you want to do a join but not include the joined columns. Do not forget to include the primary
-
# and foreign keys, otherwise it will raise an error.
-
# [:readonly]
-
# If true, all the associated objects are readonly through the association.
-
# [:validate]
-
# If +false+, don't validate the associated objects when saving the parent object. +true+ by default.
-
# [:autosave]
-
# If true, always save the associated objects or destroy them if marked for destruction, when
-
# saving the parent object.
-
# If false, never save or destroy the associated objects.
-
# By default, only save associated objects that are new records.
-
#
-
# Option examples:
-
# has_and_belongs_to_many :projects
-
# has_and_belongs_to_many :projects, :include => [ :milestones, :manager ]
-
# has_and_belongs_to_many :nations, :class_name => "Country"
-
# has_and_belongs_to_many :categories, :join_table => "prods_cats"
-
# has_and_belongs_to_many :categories, :readonly => true
-
# has_and_belongs_to_many :active_projects, :join_table => 'developers_projects', :delete_sql =>
-
# 'DELETE FROM developers_projects WHERE active=1 AND developer_id = #{id} AND project_id = #{record.id}'
-
1
def has_and_belongs_to_many(name, options = {}, &extension)
-
Builder::HasAndBelongsToMany.build(self, name, options, &extension)
-
end
-
end
-
end
-
end
-
1
module ActiveRecord::Associations::Builder
-
1
class Association #:nodoc:
-
1
class_attribute :valid_options
-
1
self.valid_options = [:class_name, :foreign_key, :select, :conditions, :include, :extend, :readonly, :validate]
-
-
# Set by subclasses
-
1
class_attribute :macro
-
-
1
attr_reader :model, :name, :options, :reflection
-
-
1
def self.build(model, name, options)
-
5
new(model, name, options).build
-
end
-
-
1
def initialize(model, name, options)
-
5
@model, @name, @options = model, name, options
-
end
-
-
1
def build
-
5
validate_options
-
5
reflection = model.create_reflection(self.class.macro, name, options, model)
-
5
define_accessors
-
5
reflection
-
end
-
-
1
private
-
-
1
def validate_options
-
2
options.assert_valid_keys(self.class.valid_options)
-
end
-
-
1
def define_accessors
-
5
define_readers
-
5
define_writers
-
end
-
-
1
def define_readers
-
5
name = self.name
-
-
5
model.redefine_method(name) do |*params|
-
association(name).reader(*params)
-
end
-
end
-
-
1
def define_writers
-
5
name = self.name
-
-
5
model.redefine_method("#{name}=") do |value|
-
association(name).writer(value)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/inclusion'
-
-
1
module ActiveRecord::Associations::Builder
-
1
class BelongsTo < SingularAssociation #:nodoc:
-
1
self.macro = :belongs_to
-
-
1
self.valid_options += [:foreign_type, :polymorphic, :touch]
-
-
1
def constructable?
-
2
!options[:polymorphic]
-
end
-
-
1
def build
-
2
reflection = super
-
2
add_counter_cache_callbacks(reflection) if options[:counter_cache]
-
2
add_touch_callbacks(reflection) if options[:touch]
-
2
configure_dependency
-
2
reflection
-
end
-
-
1
private
-
-
1
def add_counter_cache_callbacks(reflection)
-
cache_column = reflection.counter_cache_column
-
name = self.name
-
-
method_name = "belongs_to_counter_cache_after_create_for_#{name}"
-
model.redefine_method(method_name) do
-
record = send(name)
-
record.class.increment_counter(cache_column, record.id) unless record.nil?
-
end
-
model.after_create(method_name)
-
-
method_name = "belongs_to_counter_cache_before_destroy_for_#{name}"
-
model.redefine_method(method_name) do
-
record = send(name)
-
record.class.decrement_counter(cache_column, record.id) unless record.nil?
-
end
-
model.before_destroy(method_name)
-
-
model.send(:module_eval,
-
"#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)", __FILE__, __LINE__
-
)
-
end
-
-
1
def add_touch_callbacks(reflection)
-
name = self.name
-
method_name = "belongs_to_touch_after_save_or_destroy_for_#{name}"
-
touch = options[:touch]
-
-
model.redefine_method(method_name) do
-
record = send(name)
-
-
unless record.nil?
-
if touch == true
-
record.touch
-
else
-
record.touch(touch)
-
end
-
end
-
end
-
-
model.after_save(method_name)
-
model.after_touch(method_name)
-
model.after_destroy(method_name)
-
end
-
-
1
def configure_dependency
-
2
if options[:dependent]
-
unless options[:dependent].in?([:destroy, :delete])
-
raise ArgumentError, "The :dependent option expects either :destroy or :delete (#{options[:dependent].inspect})"
-
end
-
-
method_name = "belongs_to_dependent_#{options[:dependent]}_for_#{name}"
-
model.send(:class_eval, <<-eoruby, __FILE__, __LINE__ + 1)
-
def #{method_name}
-
association = #{name}
-
association.#{options[:dependent]} if association
-
end
-
eoruby
-
model.after_destroy method_name
-
end
-
end
-
end
-
end
-
1
module ActiveRecord::Associations::Builder
-
1
class CollectionAssociation < Association #:nodoc:
-
1
CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
-
-
1
self.valid_options += [
-
:table_name, :order, :group, :having, :limit, :offset, :uniq, :finder_sql,
-
:counter_sql, :before_add, :after_add, :before_remove, :after_remove
-
]
-
-
1
attr_reader :block_extension
-
-
1
def self.build(model, name, options, &extension)
-
new(model, name, options, &extension).build
-
end
-
-
1
def initialize(model, name, options, &extension)
-
super(model, name, options)
-
@block_extension = extension
-
end
-
-
1
def build
-
wrap_block_extension
-
reflection = super
-
CALLBACKS.each { |callback_name| define_callback(callback_name) }
-
reflection
-
end
-
-
1
def writable?
-
true
-
end
-
-
1
private
-
-
1
def wrap_block_extension
-
options[:extend] = Array.wrap(options[:extend])
-
-
if block_extension
-
silence_warnings do
-
model.parent.const_set(extension_module_name, Module.new(&block_extension))
-
end
-
options[:extend].push("#{model.parent}::#{extension_module_name}".constantize)
-
end
-
end
-
-
1
def extension_module_name
-
@extension_module_name ||= "#{model.to_s.demodulize}#{name.to_s.camelize}AssociationExtension"
-
end
-
-
1
def define_callback(callback_name)
-
full_callback_name = "#{callback_name}_for_#{name}"
-
-
# TODO : why do i need method_defined? I think its because of the inheritance chain
-
model.class_attribute full_callback_name.to_sym unless model.method_defined?(full_callback_name)
-
model.send("#{full_callback_name}=", Array.wrap(options[callback_name.to_sym]))
-
end
-
-
1
def define_readers
-
super
-
-
name = self.name
-
model.redefine_method("#{name.to_s.singularize}_ids") do
-
association(name).ids_reader
-
end
-
end
-
-
1
def define_writers
-
super
-
-
name = self.name
-
model.redefine_method("#{name.to_s.singularize}_ids=") do |ids|
-
association(name).ids_writer(ids)
-
end
-
end
-
end
-
end
-
1
module ActiveRecord::Associations::Builder
-
1
class HasAndBelongsToMany < CollectionAssociation #:nodoc:
-
1
self.macro = :has_and_belongs_to_many
-
-
1
self.valid_options += [:join_table, :association_foreign_key, :delete_sql, :insert_sql]
-
-
1
def build
-
reflection = super
-
check_validity(reflection)
-
define_destroy_hook
-
reflection
-
end
-
-
1
private
-
-
1
def define_destroy_hook
-
name = self.name
-
model.send(:include, Module.new {
-
class_eval <<-RUBY, __FILE__, __LINE__ + 1
-
def destroy_associations
-
association(#{name.to_sym.inspect}).delete_all
-
super
-
end
-
RUBY
-
})
-
end
-
-
# TODO: These checks should probably be moved into the Reflection, and we should not be
-
# redefining the options[:join_table] value - instead we should define a
-
# reflection.join_table method.
-
1
def check_validity(reflection)
-
if reflection.association_foreign_key == reflection.foreign_key
-
raise ActiveRecord::HasAndBelongsToManyAssociationForeignKeyNeeded.new(reflection)
-
end
-
-
reflection.options[:join_table] ||= join_table_name(
-
model.send(:undecorated_table_name, model.to_s),
-
model.send(:undecorated_table_name, reflection.class_name)
-
)
-
-
if model.connection.supports_primary_key? && (model.connection.primary_key(reflection.options[:join_table]) rescue false)
-
raise ActiveRecord::HasAndBelongsToManyAssociationWithPrimaryKeyError.new(reflection)
-
end
-
end
-
-
# Generates a join table name from two provided table names.
-
# The names in the join table names end up in lexicographic order.
-
#
-
# join_table_name("members", "clubs") # => "clubs_members"
-
# join_table_name("members", "special_clubs") # => "members_special_clubs"
-
1
def join_table_name(first_table_name, second_table_name)
-
if first_table_name < second_table_name
-
join_table = "#{first_table_name}_#{second_table_name}"
-
else
-
join_table = "#{second_table_name}_#{first_table_name}"
-
end
-
-
model.table_name_prefix + join_table + model.table_name_suffix
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/inclusion'
-
-
1
module ActiveRecord::Associations::Builder
-
1
class HasMany < CollectionAssociation #:nodoc:
-
1
self.macro = :has_many
-
-
1
self.valid_options += [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of]
-
-
1
def build
-
reflection = super
-
configure_dependency
-
reflection
-
end
-
-
1
private
-
-
1
def configure_dependency
-
if options[:dependent]
-
unless options[:dependent].in?([:destroy, :delete_all, :nullify, :restrict])
-
raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, " \
-
":nullify or :restrict (#{options[:dependent].inspect})"
-
end
-
-
send("define_#{options[:dependent]}_dependency_method")
-
model.before_destroy dependency_method_name
-
end
-
end
-
-
1
def define_destroy_dependency_method
-
name = self.name
-
model.send(:define_method, dependency_method_name) do
-
send(name).each do |o|
-
# No point in executing the counter update since we're going to destroy the parent anyway
-
counter_method = ('belongs_to_counter_cache_before_destroy_for_' + self.class.name.downcase).to_sym
-
if o.respond_to?(counter_method)
-
class << o
-
self
-
end.send(:define_method, counter_method, Proc.new {})
-
end
-
end
-
-
send(name).delete_all
-
end
-
end
-
-
1
def define_delete_all_dependency_method
-
name = self.name
-
model.send(:define_method, dependency_method_name) do
-
send(name).delete_all
-
end
-
end
-
1
alias :define_nullify_dependency_method :define_delete_all_dependency_method
-
-
1
def define_restrict_dependency_method
-
name = self.name
-
model.send(:define_method, dependency_method_name) do
-
raise ActiveRecord::DeleteRestrictionError.new(name) unless send(name).empty?
-
end
-
end
-
-
1
def dependency_method_name
-
"has_many_dependent_for_#{name}"
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/inclusion'
-
-
1
module ActiveRecord::Associations::Builder
-
1
class HasOne < SingularAssociation #:nodoc:
-
1
self.macro = :has_one
-
-
1
self.valid_options += [:order, :as]
-
-
1
class_attribute :through_options
-
1
self.through_options = [:through, :source, :source_type]
-
-
1
def constructable?
-
3
!options[:through]
-
end
-
-
1
def build
-
3
reflection = super
-
3
configure_dependency unless options[:through]
-
3
reflection
-
end
-
-
1
private
-
-
1
def validate_options
-
3
valid_options = self.class.valid_options
-
3
valid_options += self.class.through_options if options[:through]
-
3
options.assert_valid_keys(valid_options)
-
end
-
-
1
def configure_dependency
-
3
if options[:dependent]
-
2
unless options[:dependent].in?([:destroy, :delete, :nullify, :restrict])
-
raise ArgumentError, "The :dependent option expects either :destroy, :delete, " \
-
":nullify or :restrict (#{options[:dependent].inspect})"
-
end
-
-
2
send("define_#{options[:dependent]}_dependency_method")
-
2
model.before_destroy dependency_method_name
-
end
-
end
-
-
1
def dependency_method_name
-
4
"has_one_dependent_#{options[:dependent]}_for_#{name}"
-
end
-
-
1
def define_destroy_dependency_method
-
2
model.send(:class_eval, <<-eoruby, __FILE__, __LINE__ + 1)
-
def #{dependency_method_name}
-
association(#{name.to_sym.inspect}).delete
-
end
-
eoruby
-
end
-
1
alias :define_delete_dependency_method :define_destroy_dependency_method
-
1
alias :define_nullify_dependency_method :define_destroy_dependency_method
-
-
1
def define_restrict_dependency_method
-
name = self.name
-
model.redefine_method(dependency_method_name) do
-
raise ActiveRecord::DeleteRestrictionError.new(name) unless send(name).nil?
-
end
-
end
-
end
-
end
-
1
module ActiveRecord::Associations::Builder
-
1
class SingularAssociation < Association #:nodoc:
-
1
self.valid_options += [:remote, :dependent, :counter_cache, :primary_key, :inverse_of]
-
-
1
def constructable?
-
true
-
end
-
-
1
def define_accessors
-
5
super
-
5
define_constructors if constructable?
-
end
-
-
1
private
-
-
1
def define_readers
-
5
super
-
5
name = self.name
-
-
5
model.redefine_method("#{name}_loaded?") do
-
ActiveSupport::Deprecation.warn(
-
"Calling obj.#{name}_loaded? is deprecated. Please use " \
-
"obj.association(:#{name}).loaded? instead."
-
)
-
association(name).loaded?
-
end
-
end
-
-
1
def define_constructors
-
4
name = self.name
-
-
4
model.redefine_method("build_#{name}") do |*params, &block|
-
association(name).build(*params, &block)
-
end
-
-
4
model.redefine_method("create_#{name}") do |*params, &block|
-
association(name).create(*params, &block)
-
end
-
-
4
model.redefine_method("create_#{name}!") do |*params, &block|
-
association(name).create!(*params, &block)
-
end
-
end
-
end
-
end
-
1
require 'active_support/deprecation'
-
-
1
module ActiveRecord
-
1
module Associations
-
1
AssociationCollection = ActiveSupport::Deprecation::DeprecatedConstantProxy.new(
-
'ActiveRecord::Associations::AssociationCollection',
-
'ActiveRecord::Associations::CollectionProxy'
-
)
-
-
# Association proxies in Active Record are middlemen between the object that
-
# holds the association, known as the <tt>@owner</tt>, and the actual associated
-
# object, known as the <tt>@target</tt>. The kind of association any proxy is
-
# about is available in <tt>@reflection</tt>. That's an instance of the class
-
# ActiveRecord::Reflection::AssociationReflection.
-
#
-
# For example, given
-
#
-
# class Blog < ActiveRecord::Base
-
# has_many :posts
-
# end
-
#
-
# blog = Blog.first
-
#
-
# the association proxy in <tt>blog.posts</tt> has the object in +blog+ as
-
# <tt>@owner</tt>, the collection of its posts as <tt>@target</tt>, and
-
# the <tt>@reflection</tt> object represents a <tt>:has_many</tt> macro.
-
#
-
# This class has most of the basic instance methods removed, and delegates
-
# unknown methods to <tt>@target</tt> via <tt>method_missing</tt>. As a
-
# corner case, it even removes the +class+ method and that's why you get
-
#
-
# blog.posts.class # => Array
-
#
-
# though the object behind <tt>blog.posts</tt> is not an Array, but an
-
# ActiveRecord::Associations::HasManyAssociation.
-
#
-
# The <tt>@target</tt> object is not \loaded until needed. For example,
-
#
-
# blog.posts.count
-
#
-
# is computed directly through SQL and does not trigger by itself the
-
# instantiation of the actual post records.
-
1
class CollectionProxy # :nodoc:
-
1
alias :proxy_extend :extend
-
-
114
instance_methods.each { |m| undef_method m unless m.to_s =~ /^(?:nil\?|send|object_id|to_a)$|^__|^respond_to|proxy_/ }
-
-
1
delegate :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from,
-
:lock, :readonly, :having, :to => :scoped
-
-
1
delegate :target, :load_target, :loaded?, :scoped,
-
:to => :@association
-
-
1
delegate :select, :find, :first, :last,
-
:build, :create, :create!,
-
:concat, :delete_all, :destroy_all, :delete, :destroy, :uniq,
-
:sum, :count, :size, :length, :empty?,
-
:any?, :many?, :include?,
-
:to => :@association
-
-
1
def initialize(association)
-
@association = association
-
Array.wrap(association.options[:extend]).each { |ext| proxy_extend(ext) }
-
end
-
-
1
alias_method :new, :build
-
-
1
def proxy_association
-
@association
-
end
-
-
1
def respond_to?(name, include_private = false)
-
super ||
-
(load_target && target.respond_to?(name, include_private)) ||
-
proxy_association.klass.respond_to?(name, include_private)
-
end
-
-
1
def method_missing(method, *args, &block)
-
match = DynamicFinderMatch.match(method)
-
if match && match.instantiator?
-
record = send(:find_or_instantiator_by_attributes, match, match.attribute_names, *args) do |r|
-
proxy_association.send :set_owner_attributes, r
-
proxy_association.send :add_to_target, r
-
yield(r) if block_given?
-
end
-
end
-
-
if target.respond_to?(method) || (!proxy_association.klass.respond_to?(method) && Class.respond_to?(method))
-
if load_target
-
if target.respond_to?(method)
-
target.send(method, *args, &block)
-
else
-
begin
-
super
-
rescue NoMethodError => e
-
raise e, e.message.sub(/ for #<.*$/, " via proxy for #{target}")
-
end
-
end
-
end
-
-
else
-
scoped.readonly(nil).send(method, *args, &block)
-
end
-
end
-
-
# Forwards <tt>===</tt> explicitly to the \target because the instance method
-
# removal above doesn't catch it. Loads the \target if needed.
-
1
def ===(other)
-
other === load_target
-
end
-
-
1
def to_ary
-
load_target.dup
-
end
-
1
alias_method :to_a, :to_ary
-
-
1
def <<(*records)
-
proxy_association.concat(records) && self
-
end
-
1
alias_method :push, :<<
-
-
1
def clear
-
delete_all
-
self
-
end
-
-
1
def reload
-
proxy_association.reload
-
self
-
end
-
-
1
def proxy_owner
-
ActiveSupport::Deprecation.warn(
-
"Calling record.#{@association.reflection.name}.proxy_owner is deprecated. Please use " \
-
"record.association(:#{@association.reflection.name}).owner instead. Or, from an " \
-
"association extension you can access proxy_association.owner."
-
)
-
proxy_association.owner
-
end
-
-
1
def proxy_target
-
ActiveSupport::Deprecation.warn(
-
"Calling record.#{@association.reflection.name}.proxy_target is deprecated. Please use " \
-
"record.association(:#{@association.reflection.name}).target instead. Or, from an " \
-
"association extension you can access proxy_association.target."
-
)
-
proxy_association.target
-
end
-
-
1
def proxy_reflection
-
ActiveSupport::Deprecation.warn(
-
"Calling record.#{@association.reflection.name}.proxy_reflection is deprecated. Please use " \
-
"record.association(:#{@association.reflection.name}).reflection instead. Or, from an " \
-
"association extension you can access proxy_association.reflection."
-
)
-
proxy_association.reflection
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/enumerable'
-
-
1
module ActiveRecord
-
# = Active Record Attribute Methods
-
1
module AttributeMethods #:nodoc:
-
1
extend ActiveSupport::Concern
-
1
include ActiveModel::AttributeMethods
-
-
1
module ClassMethods
-
# Generates all the attribute related methods for columns in the database
-
# accessors, mutators and query methods.
-
1
def define_attribute_methods
-
1
return if attribute_methods_generated?
-
1
super(column_names)
-
1
@attribute_methods_generated = true
-
end
-
-
1
def attribute_methods_generated?
-
19
@attribute_methods_generated ||= false
-
end
-
-
1
def undefine_attribute_methods(*args)
-
6
super
-
6
@attribute_methods_generated = false
-
end
-
-
# Checks whether the method is defined in the model or any of its subclasses
-
# that also derive from Active Record. Raises DangerousAttributeError if the
-
# method is defined by Active Record though.
-
1
def instance_method_already_implemented?(method_name)
-
63
method_name = method_name.to_s
-
63
index = ancestors.index(ActiveRecord::Base) || ancestors.length
-
@_defined_class_methods ||= ancestors.first(index).map { |m|
-
2
m.instance_methods(false) | m.private_instance_methods(false)
-
107
}.flatten.map {|m| m.to_s }.to_set
-
-
63
@@_defined_activerecord_methods ||= defined_activerecord_methods
-
63
raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord" if @@_defined_activerecord_methods.include?(method_name)
-
63
@_defined_class_methods.include?(method_name)
-
end
-
-
1
def defined_activerecord_methods
-
1
active_record = ActiveRecord::Base
-
1
super_klass = ActiveRecord::Base.superclass
-
1
methods = (active_record.instance_methods - super_klass.instance_methods) +
-
1
(active_record.private_instance_methods - super_klass.private_instance_methods)
-
280
methods.map {|m| m.to_s }.to_set
-
end
-
end
-
-
1
def method_missing(method_id, *args, &block)
-
# If we haven't generated any methods yet, generate them, then
-
# see if we've created the method we're looking for.
-
if !self.class.attribute_methods_generated?
-
self.class.define_attribute_methods
-
method_name = method_id.to_s
-
guard_private_attribute_method!(method_name, args)
-
send(method_id, *args, &block)
-
else
-
super
-
end
-
end
-
-
1
def respond_to?(name, include_private = false)
-
18
self.class.define_attribute_methods unless self.class.attribute_methods_generated?
-
18
super
-
end
-
-
1
protected
-
1
def attribute_method?(attr_name)
-
attr_name == 'id' || (defined?(@attributes) && @attributes.include?(attr_name))
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module AttributeMethods
-
1
module BeforeTypeCast
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
attribute_method_suffix "_before_type_cast"
-
end
-
-
1
def read_attribute_before_type_cast(attr_name)
-
@attributes[attr_name]
-
end
-
-
# Returns a hash of attributes before typecasting and deserialization.
-
1
def attributes_before_type_cast
-
@attributes
-
end
-
-
1
private
-
-
# Handle *_before_type_cast for method_missing.
-
1
def attribute_before_type_cast(attribute_name)
-
if attribute_name == 'id'
-
read_attribute_before_type_cast(self.class.primary_key)
-
else
-
read_attribute_before_type_cast(attribute_name)
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/object/blank'
-
-
1
module ActiveRecord
-
1
module AttributeMethods
-
1
module Dirty
-
1
extend ActiveSupport::Concern
-
1
include ActiveModel::Dirty
-
1
include AttributeMethods::Write
-
-
1
included do
-
1
if self < ::ActiveRecord::Timestamp
-
raise "You cannot include Dirty after Timestamp"
-
end
-
-
1
class_attribute :partial_updates
-
1
self.partial_updates = true
-
end
-
-
# Attempts to +save+ the record and clears changed attributes if successful.
-
1
def save(*) #:nodoc:
-
if status = super
-
@previously_changed = changes
-
@changed_attributes.clear
-
elsif IdentityMap.enabled?
-
IdentityMap.remove(self)
-
end
-
status
-
end
-
-
# Attempts to <tt>save!</tt> the record and clears changed attributes if successful.
-
1
def save!(*) #:nodoc:
-
super.tap do
-
@previously_changed = changes
-
@changed_attributes.clear
-
end
-
rescue
-
IdentityMap.remove(self) if IdentityMap.enabled?
-
raise
-
end
-
-
# <tt>reload</tt> the record and clears changed attributes.
-
1
def reload(*) #:nodoc:
-
super.tap do
-
@previously_changed.clear
-
@changed_attributes.clear
-
end
-
end
-
-
1
private
-
# Wrap write_attribute to remember original attribute value.
-
1
def write_attribute(attr, value)
-
9
attr = attr.to_s
-
-
# The attribute already has an unsaved change.
-
9
if attribute_changed?(attr)
-
old = @changed_attributes[attr]
-
@changed_attributes.delete(attr) unless field_changed?(attr, old, value)
-
else
-
9
old = clone_attribute_value(:read_attribute, attr)
-
# Save Time objects as TimeWithZone if time_zone_aware_attributes == true
-
9
old = old.in_time_zone if clone_with_time_zone_conversion_attribute?(attr, old)
-
9
@changed_attributes[attr] = old if field_changed?(attr, old, value)
-
end
-
-
# Carry on.
-
9
super(attr, value)
-
end
-
-
1
def update(*)
-
if partial_updates?
-
# Serialized attributes should always be written in case they've been
-
# changed in place.
-
super(changed | (attributes.keys & self.class.serialized_attributes.keys))
-
else
-
super
-
end
-
end
-
-
1
def field_changed?(attr, old, value)
-
9
if column = column_for_attribute(attr)
-
9
if column.number? && column.null && (old.nil? || old == 0) && value.blank?
-
# For nullable numeric columns, NULL gets stored in database for blank (i.e. '') values.
-
# Hence we don't record it as a change if the value changes from nil to ''.
-
# If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
-
# be typecast back to 0 (''.to_i => 0)
-
value = nil
-
else
-
9
value = column.type_cast(value)
-
end
-
end
-
-
9
old != value
-
end
-
-
1
def clone_with_time_zone_conversion_attribute?(attr, old)
-
9
old.class.name == "Time" && time_zone_aware_attributes && !self.skip_time_zone_conversion_for_attributes.include?(attr.to_sym)
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module AttributeMethods
-
1
module PrimaryKey
-
1
extend ActiveSupport::Concern
-
-
# Returns this record's primary key value wrapped in an Array if one is available
-
1
def to_key
-
key = send(self.class.primary_key)
-
[key] if key
-
end
-
-
1
module ClassMethods
-
# Defines the primary key field -- can be overridden in subclasses. Overwriting will negate any effect of the
-
# primary_key_prefix_type setting, though.
-
1
def primary_key
-
12
@primary_key ||= reset_primary_key
-
end
-
-
# Returns a quoted version of the primary key name, used to construct SQL statements.
-
1
def quoted_primary_key
-
@quoted_primary_key ||= connection.quote_column_name(primary_key)
-
end
-
-
1
def reset_primary_key #:nodoc:
-
1
key = self == base_class ? get_primary_key(base_class.name) :
-
base_class.primary_key
-
-
1
set_primary_key(key)
-
1
key
-
end
-
-
1
def get_primary_key(base_name) #:nodoc:
-
1
return 'id' unless base_name && !base_name.blank?
-
-
1
case primary_key_prefix_type
-
when :table_name
-
base_name.foreign_key(false)
-
when :table_name_with_underscore
-
base_name.foreign_key
-
else
-
1
if ActiveRecord::Base != self && connection.table_exists?(table_name)
-
1
connection.primary_key(table_name)
-
else
-
'id'
-
end
-
end
-
end
-
-
1
attr_accessor :original_primary_key
-
-
# Attribute writer for the primary key column
-
1
def primary_key=(value)
-
1
@quoted_primary_key = nil
-
1
@primary_key = value
-
end
-
-
# Sets the name of the primary key column to use to the given value,
-
# or (if the value is nil or false) to the value returned by the given
-
# block.
-
#
-
# class Project < ActiveRecord::Base
-
# set_primary_key "sysid"
-
# end
-
1
def set_primary_key(value = nil, &block)
-
1
@quoted_primary_key = nil
-
1
@primary_key ||= ''
-
1
self.original_primary_key = @primary_key
-
1
value &&= value.to_s
-
1
connection_pool.primary_keys[table_name] = value
-
1
self.primary_key = block_given? ? instance_eval(&block) : value
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/blank'
-
-
1
module ActiveRecord
-
1
module AttributeMethods
-
1
module Query
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
attribute_method_suffix "?"
-
end
-
-
1
def query_attribute(attr_name)
-
unless value = read_attribute(attr_name)
-
false
-
else
-
column = self.class.columns_hash[attr_name]
-
if column.nil?
-
if Numeric === value || value !~ /[^0-9]/
-
!value.to_i.zero?
-
else
-
return false if ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES.include?(value)
-
!value.blank?
-
end
-
elsif column.number?
-
!value.zero?
-
else
-
!value.blank?
-
end
-
end
-
end
-
-
1
private
-
# Handle *? for method_missing.
-
1
def attribute?(attribute_name)
-
query_attribute(attribute_name)
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module AttributeMethods
-
1
module Read
-
1
extend ActiveSupport::Concern
-
-
1
ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date]
-
-
1
included do
-
1
attribute_method_suffix ""
-
-
1
cattr_accessor :attribute_types_cached_by_default, :instance_writer => false
-
1
self.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
-
-
# Undefine id so it can be used as an attribute name
-
1
undef_method(:id) if method_defined?(:id)
-
end
-
-
1
module ClassMethods
-
# +cache_attributes+ allows you to declare which converted attribute values should
-
# be cached. Usually caching only pays off for attributes with expensive conversion
-
# methods, like time related columns (e.g. +created_at+, +updated_at+).
-
1
def cache_attributes(*attribute_names)
-
cached_attributes.merge attribute_names.map { |attr| attr.to_s }
-
end
-
-
# Returns the attributes which are cached. By default time related columns
-
# with datatype <tt>:datetime, :timestamp, :time, :date</tt> are cached.
-
1
def cached_attributes
-
14
@cached_attributes ||= columns.select { |c| cacheable_column?(c) }.map { |col| col.name }.to_set
-
end
-
-
# Returns +true+ if the provided attribute is being cached.
-
1
def cache_attribute?(attr_name)
-
5
cached_attributes.include?(attr_name)
-
end
-
-
1
protected
-
1
def define_method_attribute(attr_name)
-
5
if serialized_attributes.include?(attr_name)
-
define_read_method_for_serialized_attribute(attr_name)
-
else
-
5
define_read_method(attr_name, attr_name, columns_hash[attr_name])
-
end
-
-
5
if attr_name == primary_key && attr_name != "id"
-
define_read_method('id', attr_name, columns_hash[attr_name])
-
end
-
end
-
-
1
private
-
1
def cacheable_column?(column)
-
7
serialized_attributes.include?(column.name) || attribute_types_cached_by_default.include?(column.type)
-
end
-
-
# Define read method for serialized attribute.
-
1
def define_read_method_for_serialized_attribute(attr_name)
-
access_code = "@attributes_cache['#{attr_name}'] ||= @attributes['#{attr_name}']"
-
generated_attribute_methods.module_eval("def _#{attr_name}; #{access_code}; end; alias #{attr_name} _#{attr_name}", __FILE__, __LINE__)
-
end
-
-
# Define an attribute reader method. Cope with nil column.
-
# method_name is the same as attr_name except when a non-standard primary key is used,
-
# we still define #id as an accessor for the key
-
1
def define_read_method(method_name, attr_name, column)
-
5
cast_code = column.type_cast_code('v')
-
5
access_code = "(v=@attributes['#{attr_name}']) && #{cast_code}"
-
-
5
unless attr_name.to_s == self.primary_key.to_s
-
4
access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless @attributes.has_key?('#{attr_name}'); ")
-
end
-
-
5
if cache_attribute?(attr_name)
-
access_code = "@attributes_cache['#{attr_name}'] ||= (#{access_code})"
-
end
-
-
# Where possible, generate the method by evalling a string, as this will result in
-
# faster accesses because it avoids the block eval and then string eval incurred
-
# by the second branch.
-
#
-
# The second, slower, branch is necessary to support instances where the database
-
# returns columns with extra stuff in (like 'my_column(omg)').
-
5
if method_name =~ ActiveModel::AttributeMethods::COMPILABLE_REGEXP
-
5
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__
-
def _#{method_name}
-
#{access_code}
-
end
-
-
alias #{method_name} _#{method_name}
-
STR
-
else
-
generated_attribute_methods.module_eval do
-
define_method("_#{method_name}") { eval(access_code) }
-
alias_method(method_name, "_#{method_name}")
-
end
-
end
-
end
-
end
-
-
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
-
# "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
-
1
def read_attribute(attr_name)
-
9
method = "_#{attr_name}"
-
9
if respond_to? method
-
9
send method if @attributes.has_key?(attr_name.to_s)
-
else
-
_read_attribute attr_name
-
end
-
end
-
-
1
def _read_attribute(attr_name)
-
attr_name = attr_name.to_s
-
attr_name = self.class.primary_key if attr_name == 'id'
-
value = @attributes[attr_name]
-
unless value.nil?
-
if column = column_for_attribute(attr_name)
-
if unserializable_attribute?(attr_name, column)
-
unserialize_attribute(attr_name)
-
else
-
column.type_cast(value)
-
end
-
else
-
value
-
end
-
end
-
end
-
-
# Returns true if the attribute is of a text column and marked for serialization.
-
1
def unserializable_attribute?(attr_name, column)
-
column.text? && self.class.serialized_attributes.include?(attr_name)
-
end
-
-
# Returns the unserialized object of the attribute.
-
1
def unserialize_attribute(attr_name)
-
coder = self.class.serialized_attributes[attr_name]
-
unserialized_object = coder.load(@attributes[attr_name])
-
-
@attributes.frozen? ? unserialized_object : @attributes[attr_name] = unserialized_object
-
end
-
-
1
private
-
1
def attribute(attribute_name)
-
read_attribute(attribute_name)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/object/inclusion'
-
-
1
module ActiveRecord
-
1
module AttributeMethods
-
1
module TimeZoneConversion
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
cattr_accessor :time_zone_aware_attributes, :instance_writer => false
-
1
self.time_zone_aware_attributes = false
-
-
1
class_attribute :skip_time_zone_conversion_for_attributes, :instance_writer => false
-
1
self.skip_time_zone_conversion_for_attributes = []
-
end
-
-
1
module ClassMethods
-
1
protected
-
# Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
-
# This enhanced read method automatically converts the UTC time stored in the database to the time
-
# zone stored in Time.zone.
-
1
def define_method_attribute(attr_name)
-
7
if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
-
2
method_body, line = <<-EOV, __LINE__ + 1
-
def _#{attr_name}
-
cached = @attributes_cache['#{attr_name}']
-
return cached if cached
-
time = _read_attribute('#{attr_name}')
-
@attributes_cache['#{attr_name}'] = time.acts_like?(:time) ? time.in_time_zone : time
-
end
-
alias #{attr_name} _#{attr_name}
-
EOV
-
2
generated_attribute_methods.module_eval(method_body, __FILE__, line)
-
else
-
5
super
-
end
-
end
-
-
# Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
-
# This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone.
-
1
def define_method_attribute=(attr_name)
-
7
if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
-
2
method_body, line = <<-EOV, __LINE__ + 1
-
def #{attr_name}=(original_time)
-
time = original_time
-
unless time.acts_like?(:time)
-
time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
-
end
-
time = time.in_time_zone rescue nil if time
-
write_attribute(:#{attr_name}, original_time)
-
@attributes_cache["#{attr_name}"] = time
-
end
-
EOV
-
2
generated_attribute_methods.module_eval(method_body, __FILE__, line)
-
else
-
5
super
-
end
-
end
-
-
1
private
-
1
def create_time_zone_conversion_attribute?(name, column)
-
14
time_zone_aware_attributes && !self.skip_time_zone_conversion_for_attributes.include?(name.to_sym) && column.type.in?([:datetime, :timestamp])
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module AttributeMethods
-
1
module Write
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
attribute_method_suffix "="
-
end
-
-
1
module ClassMethods
-
1
protected
-
1
def define_method_attribute=(attr_name)
-
5
if attr_name =~ ActiveModel::AttributeMethods::COMPILABLE_REGEXP
-
5
generated_attribute_methods.module_eval("def #{attr_name}=(new_value); write_attribute('#{attr_name}', new_value); end", __FILE__, __LINE__)
-
else
-
generated_attribute_methods.send(:define_method, "#{attr_name}=") do |new_value|
-
write_attribute(attr_name, new_value)
-
end
-
end
-
end
-
end
-
-
# Updates the attribute identified by <tt>attr_name</tt> with the specified +value+. Empty strings
-
# for fixnum and float columns are turned into +nil+.
-
1
def write_attribute(attr_name, value)
-
9
attr_name = attr_name.to_s
-
9
attr_name = self.class.primary_key if attr_name == 'id'
-
9
@attributes_cache.delete(attr_name)
-
9
if (column = column_for_attribute(attr_name)) && column.number?
-
@attributes[attr_name] = convert_number_column_value(value)
-
else
-
9
@attributes[attr_name] = value
-
end
-
end
-
1
alias_method :raw_write_attribute, :write_attribute
-
-
1
private
-
# Handle *= for method_missing.
-
1
def attribute=(attribute_name, value)
-
write_attribute(attribute_name, value)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/array/wrap'
-
-
1
module ActiveRecord
-
# = Active Record Autosave Association
-
#
-
# +AutosaveAssociation+ is a module that takes care of automatically saving
-
# associated records when their parent is saved. In addition to saving, it
-
# also destroys any associated records that were marked for destruction.
-
# (See +mark_for_destruction+ and <tt>marked_for_destruction?</tt>).
-
#
-
# Saving of the parent, its associations, and the destruction of marked
-
# associations, all happen inside a transaction. This should never leave the
-
# database in an inconsistent state.
-
#
-
# If validations for any of the associations fail, their error messages will
-
# be applied to the parent.
-
#
-
# Note that it also means that associations marked for destruction won't
-
# be destroyed directly. They will however still be marked for destruction.
-
#
-
# Note that <tt>:autosave => false</tt> is not same as not declaring <tt>:autosave</tt>.
-
# When the <tt>:autosave</tt> option is not present new associations are saved.
-
#
-
# === One-to-one Example
-
#
-
# class Post
-
# has_one :author, :autosave => true
-
# end
-
#
-
# Saving changes to the parent and its associated model can now be performed
-
# automatically _and_ atomically:
-
#
-
# post = Post.find(1)
-
# post.title # => "The current global position of migrating ducks"
-
# post.author.name # => "alloy"
-
#
-
# post.title = "On the migration of ducks"
-
# post.author.name = "Eloy Duran"
-
#
-
# post.save
-
# post.reload
-
# post.title # => "On the migration of ducks"
-
# post.author.name # => "Eloy Duran"
-
#
-
# Destroying an associated model, as part of the parent's save action, is as
-
# simple as marking it for destruction:
-
#
-
# post.author.mark_for_destruction
-
# post.author.marked_for_destruction? # => true
-
#
-
# Note that the model is _not_ yet removed from the database:
-
#
-
# id = post.author.id
-
# Author.find_by_id(id).nil? # => false
-
#
-
# post.save
-
# post.reload.author # => nil
-
#
-
# Now it _is_ removed from the database:
-
#
-
# Author.find_by_id(id).nil? # => true
-
#
-
# === One-to-many Example
-
#
-
# When <tt>:autosave</tt> is not declared new children are saved when their parent is saved:
-
#
-
# class Post
-
# has_many :comments # :autosave option is no declared
-
# end
-
#
-
# post = Post.new(:title => 'ruby rocks')
-
# post.comments.build(:body => 'hello world')
-
# post.save # => saves both post and comment
-
#
-
# post = Post.create(:title => 'ruby rocks')
-
# post.comments.build(:body => 'hello world')
-
# post.save # => saves both post and comment
-
#
-
# post = Post.create(:title => 'ruby rocks')
-
# post.comments.create(:body => 'hello world')
-
# post.save # => saves both post and comment
-
#
-
# When <tt>:autosave</tt> is true all children is saved, no matter whether they are new records:
-
#
-
# class Post
-
# has_many :comments, :autosave => true
-
# end
-
#
-
# post = Post.create(:title => 'ruby rocks')
-
# post.comments.create(:body => 'hello world')
-
# post.comments[0].body = 'hi everyone'
-
# post.save # => saves both post and comment, with 'hi everyone' as body
-
#
-
# Destroying one of the associated models as part of the parent's save action
-
# is as simple as marking it for destruction:
-
#
-
# post.comments.last.mark_for_destruction
-
# post.comments.last.marked_for_destruction? # => true
-
# post.comments.length # => 2
-
#
-
# Note that the model is _not_ yet removed from the database:
-
#
-
# id = post.comments.last.id
-
# Comment.find_by_id(id).nil? # => false
-
#
-
# post.save
-
# post.reload.comments.length # => 1
-
#
-
# Now it _is_ removed from the database:
-
#
-
# Comment.find_by_id(id).nil? # => true
-
#
-
# === Validation
-
#
-
# Children records are validated unless <tt>:validate</tt> is +false+.
-
1
module AutosaveAssociation
-
1
extend ActiveSupport::Concern
-
-
1
ASSOCIATION_TYPES = %w{ HasOne HasMany BelongsTo HasAndBelongsToMany }
-
-
1
module AssociationBuilderExtension #:nodoc:
-
1
def self.included(base)
-
4
base.valid_options << :autosave
-
end
-
-
1
def build
-
5
reflection = super
-
5
model.send(:add_autosave_association_callbacks, reflection)
-
5
reflection
-
end
-
end
-
-
1
included do
-
1
ASSOCIATION_TYPES.each do |type|
-
4
Associations::Builder.const_get(type).send(:include, AssociationBuilderExtension)
-
end
-
end
-
-
1
module ClassMethods
-
1
private
-
-
1
def define_non_cyclic_method(name, reflection, &block)
-
4
define_method(name) do |*args|
-
result = true; @_already_called ||= {}
-
# Loop prevention for validation of associations
-
unless @_already_called[[name, reflection.name]]
-
begin
-
@_already_called[[name, reflection.name]]=true
-
result = instance_eval(&block)
-
ensure
-
@_already_called[[name, reflection.name]]=false
-
end
-
end
-
-
result
-
end
-
end
-
-
# Adds validation and save callbacks for the association as specified by
-
# the +reflection+.
-
#
-
# For performance reasons, we don't check whether to validate at runtime.
-
# However the validation and callback methods are lazy and those methods
-
# get created when they are invoked for the very first time. However,
-
# this can change, for instance, when using nested attributes, which is
-
# called _after_ the association has been defined. Since we don't want
-
# the callbacks to get defined multiple times, there are guards that
-
# check if the save or validation methods have already been defined
-
# before actually defining them.
-
1
def add_autosave_association_callbacks(reflection)
-
7
save_method = :"autosave_associated_records_for_#{reflection.name}"
-
7
validation_method = :"validate_associated_records_for_#{reflection.name}"
-
7
collection = reflection.collection?
-
-
7
unless method_defined?(save_method)
-
5
if collection
-
before_save :before_save_collection_association
-
-
define_non_cyclic_method(save_method, reflection) { save_collection_association(reflection) }
-
# Doesn't use after_save as that would save associations added in after_create/after_update twice
-
after_create save_method
-
after_update save_method
-
else
-
5
if reflection.macro == :has_one
-
3
define_method(save_method) { save_has_one_association(reflection) }
-
# Configures two callbacks instead of a single after_save so that
-
# the model may rely on their execution order relative to its
-
# own callbacks.
-
#
-
# For example, given that after_creates run before after_saves, if
-
# we configured instead an after_save there would be no way to fire
-
# a custom after_create callback after the child association gets
-
# created.
-
3
after_create save_method
-
3
after_update save_method
-
else
-
2
define_non_cyclic_method(save_method, reflection) { save_belongs_to_association(reflection) }
-
2
before_save save_method
-
end
-
end
-
end
-
-
7
if reflection.validate? && !method_defined?(validation_method)
-
2
method = (collection ? :validate_collection_association : :validate_single_association)
-
2
define_non_cyclic_method(validation_method, reflection) { send(method, reflection) }
-
2
validate validation_method
-
end
-
end
-
end
-
-
# Reloads the attributes of the object as usual and clears <tt>marked_for_destruction</tt> flag.
-
1
def reload(options = nil)
-
@marked_for_destruction = false
-
super
-
end
-
-
# Marks this record to be destroyed as part of the parents save transaction.
-
# This does _not_ actually destroy the record instantly, rather child record will be destroyed
-
# when <tt>parent.save</tt> is called.
-
#
-
# Only useful if the <tt>:autosave</tt> option on the parent is enabled for this associated model.
-
1
def mark_for_destruction
-
@marked_for_destruction = true
-
end
-
-
# Returns whether or not this record will be destroyed as part of the parents save transaction.
-
#
-
# Only useful if the <tt>:autosave</tt> option on the parent is enabled for this associated model.
-
1
def marked_for_destruction?
-
@marked_for_destruction
-
end
-
-
# Returns whether or not this record has been changed in any way (including whether
-
# any of its nested autosave associations are likewise changed)
-
1
def changed_for_autosave?
-
new_record? || changed? || marked_for_destruction? || nested_records_changed_for_autosave?
-
end
-
-
1
private
-
-
# Returns the record for an association collection that should be validated
-
# or saved. If +autosave+ is +false+ only new records will be returned,
-
# unless the parent is/was a new record itself.
-
1
def associated_records_to_validate_or_save(association, new_record, autosave)
-
if new_record
-
association && association.target
-
elsif autosave
-
association.target.find_all { |record| record.changed_for_autosave? }
-
else
-
association.target.find_all { |record| record.new_record? }
-
end
-
end
-
-
# go through nested autosave associations that are loaded in memory (without loading
-
# any new ones), and return true if is changed for autosave
-
1
def nested_records_changed_for_autosave?
-
self.class.reflect_on_all_autosave_associations.any? do |reflection|
-
association = association_instance_get(reflection.name)
-
association && Array.wrap(association.target).any? { |a| a.changed_for_autosave? }
-
end
-
end
-
-
# Validate the association if <tt>:validate</tt> or <tt>:autosave</tt> is
-
# turned on for the association.
-
1
def validate_single_association(reflection)
-
association = association_instance_get(reflection.name)
-
record = association && association.target
-
association_valid?(reflection, record) if record
-
end
-
-
# Validate the associated records if <tt>:validate</tt> or
-
# <tt>:autosave</tt> is turned on for the association specified by
-
# +reflection+.
-
1
def validate_collection_association(reflection)
-
if association = association_instance_get(reflection.name)
-
if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
-
records.each { |record| association_valid?(reflection, record) }
-
end
-
end
-
end
-
-
# Returns whether or not the association is valid and applies any errors to
-
# the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
-
# enabled records if they're marked_for_destruction? or destroyed.
-
1
def association_valid?(reflection, record)
-
return true if record.destroyed? || record.marked_for_destruction?
-
-
unless valid = record.valid?
-
if reflection.options[:autosave]
-
record.errors.each do |attribute, message|
-
attribute = "#{reflection.name}.#{attribute}"
-
errors[attribute] << message
-
errors[attribute].uniq!
-
end
-
else
-
errors.add(reflection.name)
-
end
-
end
-
valid
-
end
-
-
# Is used as a before_save callback to check while saving a collection
-
# association whether or not the parent was a new record before saving.
-
1
def before_save_collection_association
-
@new_record_before_save = new_record?
-
true
-
end
-
-
# Saves any new associated records, or all loaded autosave associations if
-
# <tt>:autosave</tt> is enabled on the association.
-
#
-
# In addition, it destroys all children that were marked for destruction
-
# with mark_for_destruction.
-
#
-
# This all happens inside a transaction, _if_ the Transactions module is included into
-
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
-
1
def save_collection_association(reflection)
-
if association = association_instance_get(reflection.name)
-
autosave = reflection.options[:autosave]
-
-
if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
-
begin
-
records.each do |record|
-
next if record.destroyed?
-
-
saved = true
-
-
if autosave && record.marked_for_destruction?
-
association.proxy.destroy(record)
-
elsif autosave != false && (@new_record_before_save || record.new_record?)
-
if autosave
-
saved = association.insert_record(record, false)
-
else
-
association.insert_record(record)
-
end
-
elsif autosave
-
saved = record.save(:validate => false)
-
end
-
-
raise ActiveRecord::Rollback unless saved
-
end
-
rescue
-
records.each {|x| IdentityMap.remove(x) } if IdentityMap.enabled?
-
raise
-
end
-
-
end
-
-
# reconstruct the scope now that we know the owner's id
-
association.send(:reset_scope) if association.respond_to?(:reset_scope)
-
end
-
end
-
-
# Saves the associated record if it's new or <tt>:autosave</tt> is enabled
-
# on the association.
-
#
-
# In addition, it will destroy the association if it was marked for
-
# destruction with mark_for_destruction.
-
#
-
# This all happens inside a transaction, _if_ the Transactions module is included into
-
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
-
1
def save_has_one_association(reflection)
-
association = association_instance_get(reflection.name)
-
record = association && association.load_target
-
if record && !record.destroyed?
-
autosave = reflection.options[:autosave]
-
-
if autosave && record.marked_for_destruction?
-
record.destroy
-
else
-
key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id
-
if autosave != false && (new_record? || record.new_record? || record[reflection.foreign_key] != key || autosave)
-
record[reflection.foreign_key] = key
-
saved = record.save(:validate => !autosave)
-
raise ActiveRecord::Rollback if !saved && autosave
-
saved
-
end
-
end
-
end
-
end
-
-
# Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
-
#
-
# In addition, it will destroy the association if it was marked for destruction.
-
1
def save_belongs_to_association(reflection)
-
association = association_instance_get(reflection.name)
-
record = association && association.load_target
-
if record && !record.destroyed?
-
autosave = reflection.options[:autosave]
-
-
if autosave && record.marked_for_destruction?
-
record.destroy
-
elsif autosave != false
-
saved = record.save(:validate => !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)
-
-
if association.updated?
-
association_id = record.send(reflection.options[:primary_key] || :id)
-
self[reflection.foreign_key] = association_id
-
association.loaded!
-
end
-
-
saved if autosave
-
end
-
end
-
end
-
end
-
end
-
1
begin
-
1
require 'psych'
-
rescue LoadError
-
end
-
-
1
require 'yaml'
-
1
require 'set'
-
1
require 'active_support/benchmarkable'
-
1
require 'active_support/dependencies'
-
1
require 'active_support/descendants_tracker'
-
1
require 'active_support/time'
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/class/attribute_accessors'
-
1
require 'active_support/core_ext/class/delegating_attributes'
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/array/extract_options'
-
1
require 'active_support/core_ext/hash/deep_merge'
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
1
require 'active_support/core_ext/hash/slice'
-
1
require 'active_support/core_ext/string/behavior'
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
1
require 'active_support/core_ext/module/delegation'
-
1
require 'active_support/core_ext/module/introspection'
-
1
require 'active_support/core_ext/object/duplicable'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/deprecation'
-
1
require 'arel'
-
1
require 'active_record/errors'
-
1
require 'active_record/log_subscriber'
-
-
1
module ActiveRecord #:nodoc:
-
# = Active Record
-
#
-
# Active Record objects don't specify their attributes directly, but rather infer them from
-
# the table definition with which they're linked. Adding, removing, and changing attributes
-
# and their type is done directly in the database. Any change is instantly reflected in the
-
# Active Record objects. The mapping that binds a given Active Record class to a certain
-
# database table will happen automatically in most common cases, but can be overwritten for the uncommon ones.
-
#
-
# See the mapping rules in table_name and the full example in link:files/activerecord/README_rdoc.html for more insight.
-
#
-
# == Creation
-
#
-
# Active Records accept constructor parameters either in a hash or as a block. The hash
-
# method is especially useful when you're receiving the data from somewhere else, like an
-
# HTTP request. It works like this:
-
#
-
# user = User.new(:name => "David", :occupation => "Code Artist")
-
# user.name # => "David"
-
#
-
# You can also use block initialization:
-
#
-
# user = User.new do |u|
-
# u.name = "David"
-
# u.occupation = "Code Artist"
-
# end
-
#
-
# And of course you can just create a bare object and specify the attributes after the fact:
-
#
-
# user = User.new
-
# user.name = "David"
-
# user.occupation = "Code Artist"
-
#
-
# == Conditions
-
#
-
# Conditions can either be specified as a string, array, or hash representing the WHERE-part of an SQL statement.
-
# The array form is to be used when the condition input is tainted and requires sanitization. The string form can
-
# be used for statements that don't involve tainted data. The hash form works much like the array form, except
-
# only equality and range is possible. Examples:
-
#
-
# class User < ActiveRecord::Base
-
# def self.authenticate_unsafely(user_name, password)
-
# where("user_name = '#{user_name}' AND password = '#{password}'").first
-
# end
-
#
-
# def self.authenticate_safely(user_name, password)
-
# where("user_name = ? AND password = ?", user_name, password).first
-
# end
-
#
-
# def self.authenticate_safely_simply(user_name, password)
-
# where(:user_name => user_name, :password => password).first
-
# end
-
# end
-
#
-
# The <tt>authenticate_unsafely</tt> method inserts the parameters directly into the query
-
# and is thus susceptible to SQL-injection attacks if the <tt>user_name</tt> and +password+
-
# parameters come directly from an HTTP request. The <tt>authenticate_safely</tt> and
-
# <tt>authenticate_safely_simply</tt> both will sanitize the <tt>user_name</tt> and +password+
-
# before inserting them in the query, which will ensure that an attacker can't escape the
-
# query and fake the login (or worse).
-
#
-
# When using multiple parameters in the conditions, it can easily become hard to read exactly
-
# what the fourth or fifth question mark is supposed to represent. In those cases, you can
-
# resort to named bind variables instead. That's done by replacing the question marks with
-
# symbols and supplying a hash with values for the matching symbol keys:
-
#
-
# Company.where(
-
# "id = :id AND name = :name AND division = :division AND created_at > :accounting_date",
-
# { :id => 3, :name => "37signals", :division => "First", :accounting_date => '2005-01-01' }
-
# ).first
-
#
-
# Similarly, a simple hash without a statement will generate conditions based on equality with the SQL AND
-
# operator. For instance:
-
#
-
# Student.where(:first_name => "Harvey", :status => 1)
-
# Student.where(params[:student])
-
#
-
# A range may be used in the hash to use the SQL BETWEEN operator:
-
#
-
# Student.where(:grade => 9..12)
-
#
-
# An array may be used in the hash to use the SQL IN operator:
-
#
-
# Student.where(:grade => [9,11,12])
-
#
-
# When joining tables, nested hashes or keys written in the form 'table_name.column_name'
-
# can be used to qualify the table name of a particular condition. For instance:
-
#
-
# Student.joins(:schools).where(:schools => { :type => 'public' })
-
# Student.joins(:schools).where('schools.type' => 'public' )
-
#
-
# == Overwriting default accessors
-
#
-
# All column values are automatically available through basic accessors on the Active Record
-
# object, but sometimes you want to specialize this behavior. This can be done by overwriting
-
# the default accessors (using the same name as the attribute) and calling
-
# <tt>read_attribute(attr_name)</tt> and <tt>write_attribute(attr_name, value)</tt> to actually
-
# change things.
-
#
-
# class Song < ActiveRecord::Base
-
# # Uses an integer of seconds to hold the length of the song
-
#
-
# def length=(minutes)
-
# write_attribute(:length, minutes.to_i * 60)
-
# end
-
#
-
# def length
-
# read_attribute(:length) / 60
-
# end
-
# end
-
#
-
# You can alternatively use <tt>self[:attribute]=(value)</tt> and <tt>self[:attribute]</tt>
-
# instead of <tt>write_attribute(:attribute, value)</tt> and <tt>read_attribute(:attribute)</tt>.
-
#
-
# == Attribute query methods
-
#
-
# In addition to the basic accessors, query methods are also automatically available on the Active Record object.
-
# Query methods allow you to test whether an attribute value is present.
-
#
-
# For example, an Active Record User with the <tt>name</tt> attribute has a <tt>name?</tt> method that you can call
-
# to determine whether the user has a name:
-
#
-
# user = User.new(:name => "David")
-
# user.name? # => true
-
#
-
# anonymous = User.new(:name => "")
-
# anonymous.name? # => false
-
#
-
# == Accessing attributes before they have been typecasted
-
#
-
# Sometimes you want to be able to read the raw attribute data without having the column-determined
-
# typecast run its course first. That can be done by using the <tt><attribute>_before_type_cast</tt>
-
# accessors that all attributes have. For example, if your Account model has a <tt>balance</tt> attribute,
-
# you can call <tt>account.balance_before_type_cast</tt> or <tt>account.id_before_type_cast</tt>.
-
#
-
# This is especially useful in validation situations where the user might supply a string for an
-
# integer field and you want to display the original string back in an error message. Accessing the
-
# attribute normally would typecast the string to 0, which isn't what you want.
-
#
-
# == Dynamic attribute-based finders
-
#
-
# Dynamic attribute-based finders are a cleaner way of getting (and/or creating) objects
-
# by simple queries without turning to SQL. They work by appending the name of an attribute
-
# to <tt>find_by_</tt>, <tt>find_last_by_</tt>, or <tt>find_all_by_</tt> and thus produces finders
-
# like <tt>Person.find_by_user_name</tt>, <tt>Person.find_all_by_last_name</tt>, and
-
# <tt>Payment.find_by_transaction_id</tt>. Instead of writing
-
# <tt>Person.where(:user_name => user_name).first</tt>, you just do <tt>Person.find_by_user_name(user_name)</tt>.
-
# And instead of writing <tt>Person.where(:last_name => last_name).all</tt>, you just do
-
# <tt>Person.find_all_by_last_name(last_name)</tt>.
-
#
-
# It's also possible to use multiple attributes in the same find by separating them with "_and_".
-
#
-
# Person.where(:user_name => user_name, :password => password).first
-
# Person.find_by_user_name_and_password(user_name, password) # with dynamic finder
-
#
-
# It's even possible to call these dynamic finder methods on relations and named scopes.
-
#
-
# Payment.order("created_on").find_all_by_amount(50)
-
# Payment.pending.find_last_by_amount(100)
-
#
-
# The same dynamic finder style can be used to create the object if it doesn't already exist.
-
# This dynamic finder is called with <tt>find_or_create_by_</tt> and will return the object if
-
# it already exists and otherwise creates it, then returns it. Protected attributes won't be set
-
# unless they are given in a block.
-
#
-
# # No 'Summer' tag exists
-
# Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer")
-
#
-
# # Now the 'Summer' tag does exist
-
# Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer")
-
#
-
# # Now 'Bob' exist and is an 'admin'
-
# User.find_or_create_by_name('Bob', :age => 40) { |u| u.admin = true }
-
#
-
# Use the <tt>find_or_initialize_by_</tt> finder if you want to return a new record without
-
# saving it first. Protected attributes won't be set unless they are given in a block.
-
#
-
# # No 'Winter' tag exists
-
# winter = Tag.find_or_initialize_by_name("Winter")
-
# winter.persisted? # false
-
#
-
# To find by a subset of the attributes to be used for instantiating a new object, pass a hash instead of
-
# a list of parameters.
-
#
-
# Tag.find_or_create_by_name(:name => "rails", :creator => current_user)
-
#
-
# That will either find an existing tag named "rails", or create a new one while setting the
-
# user that created it.
-
#
-
# Just like <tt>find_by_*</tt>, you can also use <tt>scoped_by_*</tt> to retrieve data. The good thing about
-
# using this feature is that the very first time result is returned using <tt>method_missing</tt> technique
-
# but after that the method is declared on the class. Henceforth <tt>method_missing</tt> will not be hit.
-
#
-
# User.scoped_by_user_name('David')
-
#
-
# == Saving arrays, hashes, and other non-mappable objects in text columns
-
#
-
# Active Record can serialize any object in text columns using YAML. To do so, you must
-
# specify this with a call to the class method +serialize+.
-
# This makes it possible to store arrays, hashes, and other non-mappable objects without doing
-
# any additional work.
-
#
-
# class User < ActiveRecord::Base
-
# serialize :preferences
-
# end
-
#
-
# user = User.create(:preferences => { "background" => "black", "display" => large })
-
# User.find(user.id).preferences # => { "background" => "black", "display" => large }
-
#
-
# You can also specify a class option as the second parameter that'll raise an exception
-
# if a serialized object is retrieved as a descendant of a class not in the hierarchy.
-
#
-
# class User < ActiveRecord::Base
-
# serialize :preferences, Hash
-
# end
-
#
-
# user = User.create(:preferences => %w( one two three ))
-
# User.find(user.id).preferences # raises SerializationTypeMismatch
-
#
-
# When you specify a class option, the default value for that attribute will be a new
-
# instance of that class.
-
#
-
# class User < ActiveRecord::Base
-
# serialize :preferences, OpenStruct
-
# end
-
#
-
# user = User.new
-
# user.preferences.theme_color = "red"
-
#
-
#
-
# == Single table inheritance
-
#
-
# Active Record allows inheritance by storing the name of the class in a column that by
-
# default is named "type" (can be changed by overwriting <tt>Base.inheritance_column</tt>).
-
# This means that an inheritance looking like this:
-
#
-
# class Company < ActiveRecord::Base; end
-
# class Firm < Company; end
-
# class Client < Company; end
-
# class PriorityClient < Client; end
-
#
-
# When you do <tt>Firm.create(:name => "37signals")</tt>, this record will be saved in
-
# the companies table with type = "Firm". You can then fetch this row again using
-
# <tt>Company.where(:name => '37signals').first</tt> and it will return a Firm object.
-
#
-
# If you don't have a type column defined in your table, single-table inheritance won't
-
# be triggered. In that case, it'll work just like normal subclasses with no special magic
-
# for differentiating between them or reloading the right type with find.
-
#
-
# Note, all the attributes for all the cases are kept in the same table. Read more:
-
# http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
-
#
-
# == Connection to multiple databases in different models
-
#
-
# Connections are usually created through ActiveRecord::Base.establish_connection and retrieved
-
# by ActiveRecord::Base.connection. All classes inheriting from ActiveRecord::Base will use this
-
# connection. But you can also set a class-specific connection. For example, if Course is an
-
# ActiveRecord::Base, but resides in a different database, you can just say <tt>Course.establish_connection</tt>
-
# and Course and all of its subclasses will use this connection instead.
-
#
-
# This feature is implemented by keeping a connection pool in ActiveRecord::Base that is
-
# a Hash indexed by the class. If a connection is requested, the retrieve_connection method
-
# will go up the class-hierarchy until a connection is found in the connection pool.
-
#
-
# == Exceptions
-
#
-
# * ActiveRecordError - Generic error class and superclass of all other errors raised by Active Record.
-
# * AdapterNotSpecified - The configuration hash used in <tt>establish_connection</tt> didn't include an
-
# <tt>:adapter</tt> key.
-
# * AdapterNotFound - The <tt>:adapter</tt> key used in <tt>establish_connection</tt> specified a
-
# non-existent adapter
-
# (or a bad spelling of an existing one).
-
# * AssociationTypeMismatch - The object assigned to the association wasn't of the type
-
# specified in the association definition.
-
# * SerializationTypeMismatch - The serialized object wasn't of the class specified as the second parameter.
-
# * ConnectionNotEstablished+ - No connection has been established. Use <tt>establish_connection</tt>
-
# before querying.
-
# * RecordNotFound - No record responded to the +find+ method. Either the row with the given ID doesn't exist
-
# or the row didn't meet the additional restrictions. Some +find+ calls do not raise this exception to signal
-
# nothing was found, please check its documentation for further details.
-
# * StatementInvalid - The database server rejected the SQL statement. The precise error is added in the message.
-
# * MultiparameterAssignmentErrors - Collection of errors that occurred during a mass assignment using the
-
# <tt>attributes=</tt> method. The +errors+ property of this exception contains an array of
-
# AttributeAssignmentError
-
# objects that should be inspected to determine which attributes triggered the errors.
-
# * AttributeAssignmentError - An error occurred while doing a mass assignment through the
-
# <tt>attributes=</tt> method.
-
# You can inspect the +attribute+ property of the exception object to determine which attribute
-
# triggered the error.
-
#
-
# *Note*: The attributes listed are class-level attributes (accessible from both the class and instance level).
-
# So it's possible to assign a logger to the class through <tt>Base.logger=</tt> which will then be used by all
-
# instances in the current object space.
-
1
class Base
-
##
-
# :singleton-method:
-
# Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class,
-
# which is then passed on to any new database connections made and which can be retrieved on both
-
# a class and instance level by calling +logger+.
-
1
cattr_accessor :logger, :instance_writer => false
-
-
##
-
# :singleton-method:
-
# Contains the database configuration - as is typically stored in config/database.yml -
-
# as a Hash.
-
#
-
# For example, the following database.yml...
-
#
-
# development:
-
# adapter: sqlite3
-
# database: db/development.sqlite3
-
#
-
# production:
-
# adapter: sqlite3
-
# database: db/production.sqlite3
-
#
-
# ...would result in ActiveRecord::Base.configurations to look like this:
-
#
-
# {
-
# 'development' => {
-
# 'adapter' => 'sqlite3',
-
# 'database' => 'db/development.sqlite3'
-
# },
-
# 'production' => {
-
# 'adapter' => 'sqlite3',
-
# 'database' => 'db/production.sqlite3'
-
# }
-
# }
-
1
cattr_accessor :configurations, :instance_writer => false
-
1
@@configurations = {}
-
-
##
-
# :singleton-method:
-
# Accessor for the prefix type that will be prepended to every primary key column name.
-
# The options are :table_name and :table_name_with_underscore. If the first is specified,
-
# the Product class will look for "productid" instead of "id" as the primary column. If the
-
# latter is specified, the Product class will look for "product_id" instead of "id". Remember
-
# that this is a global setting for all Active Records.
-
1
cattr_accessor :primary_key_prefix_type, :instance_writer => false
-
1
@@primary_key_prefix_type = nil
-
-
##
-
# :singleton-method:
-
# Accessor for the name of the prefix string to prepend to every table name. So if set
-
# to "basecamp_", all table names will be named like "basecamp_projects", "basecamp_people",
-
# etc. This is a convenient way of creating a namespace for tables in a shared database.
-
# By default, the prefix is the empty string.
-
#
-
# If you are organising your models within modules you can add a prefix to the models within
-
# a namespace by defining a singleton method in the parent module called table_name_prefix which
-
# returns your chosen prefix.
-
1
class_attribute :table_name_prefix, :instance_writer => false
-
1
self.table_name_prefix = ""
-
-
##
-
# :singleton-method:
-
# Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
-
# "people_basecamp"). By default, the suffix is the empty string.
-
1
class_attribute :table_name_suffix, :instance_writer => false
-
1
self.table_name_suffix = ""
-
-
##
-
# :singleton-method:
-
# Indicates whether table names should be the pluralized versions of the corresponding class names.
-
# If true, the default table name for a Product class will be +products+. If false, it would just be +product+.
-
# See table_name for the full rules on table/class naming. This is true, by default.
-
1
class_attribute :pluralize_table_names, :instance_writer => false
-
1
self.pluralize_table_names = true
-
-
##
-
# :singleton-method:
-
# Determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling
-
# dates and times from the database. This is set to :local by default.
-
1
cattr_accessor :default_timezone, :instance_writer => false
-
1
@@default_timezone = :local
-
-
##
-
# :singleton-method:
-
# Specifies the format to use when dumping the database schema with Rails'
-
# Rakefile. If :sql, the schema is dumped as (potentially database-
-
# specific) SQL statements. If :ruby, the schema is dumped as an
-
# ActiveRecord::Schema file which can be loaded into any database that
-
# supports migrations. Use :ruby if you want to have different database
-
# adapters for, e.g., your development and test environments.
-
1
cattr_accessor :schema_format , :instance_writer => false
-
1
@@schema_format = :ruby
-
-
##
-
# :singleton-method:
-
# Specify whether or not to use timestamps for migration versions
-
1
cattr_accessor :timestamped_migrations , :instance_writer => false
-
1
@@timestamped_migrations = true
-
-
# Determine whether to store the full constant name including namespace when using STI
-
1
class_attribute :store_full_sti_class
-
1
self.store_full_sti_class = true
-
-
# Stores the default scope for the class
-
1
class_attribute :default_scopes, :instance_writer => false
-
1
self.default_scopes = []
-
-
# Returns a hash of all the attributes that have been specified for serialization as
-
# keys and their class restriction as values.
-
1
class_attribute :serialized_attributes
-
1
self.serialized_attributes = {}
-
-
1
class_attribute :_attr_readonly, :instance_writer => false
-
1
self._attr_readonly = []
-
-
1
class << self # Class methods
-
1
delegate :find, :first, :first!, :last, :last!, :all, :exists?, :any?, :many?, :to => :scoped
-
1
delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, :to => :scoped
-
1
delegate :find_each, :find_in_batches, :to => :scoped
-
1
delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped
-
1
delegate :count, :average, :minimum, :maximum, :sum, :calculate, :to => :scoped
-
-
# Executes a custom SQL query against your database and returns all the results. The results will
-
# be returned as an array with columns requested encapsulated as attributes of the model you call
-
# this method from. If you call <tt>Product.find_by_sql</tt> then the results will be returned in
-
# a Product object with the attributes you specified in the SQL query.
-
#
-
# If you call a complicated SQL query which spans multiple tables the columns specified by the
-
# SELECT will be attributes of the model, whether or not they are columns of the corresponding
-
# table.
-
#
-
# The +sql+ parameter is a full SQL query as a string. It will be called as is, there will be
-
# no database agnostic conversions performed. This should be a last resort because using, for example,
-
# MySQL specific terms will lock you to using that particular database engine or require you to
-
# change your call if you switch engines.
-
#
-
# ==== Examples
-
# # A simple SQL query spanning multiple tables
-
# Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id"
-
# > [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "first_name"=>"Quentin"}>, ...]
-
#
-
# # You can use the same string replacement techniques as you can with ActiveRecord#find
-
# Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
-
# > [#<Post:0x36bff9c @attributes={"title"=>"The Cheap Man Buys Twice"}>, ...]
-
1
def find_by_sql(sql, binds = [])
-
connection.select_all(sanitize_sql(sql), "#{name} Load", binds).collect! { |record| instantiate(record) }
-
end
-
-
# Creates an object (or multiple objects) and saves it to the database, if validations pass.
-
# The resulting object is returned whether the object was saved successfully to the database or not.
-
#
-
# The +attributes+ parameter can be either be a Hash or an Array of Hashes. These Hashes describe the
-
# attributes on the objects that are to be created.
-
#
-
# +create+ respects mass-assignment security and accepts either +:as+ or +:without_protection+ options
-
# in the +options+ parameter.
-
#
-
# ==== Examples
-
# # Create a single new object
-
# User.create(:first_name => 'Jamie')
-
#
-
# # Create a single new object using the :admin mass-assignment security role
-
# User.create({ :first_name => 'Jamie', :is_admin => true }, :as => :admin)
-
#
-
# # Create a single new object bypassing mass-assignment security
-
# User.create({ :first_name => 'Jamie', :is_admin => true }, :without_protection => true)
-
#
-
# # Create an Array of new objects
-
# User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])
-
#
-
# # Create a single object and pass it into a block to set other attributes.
-
# User.create(:first_name => 'Jamie') do |u|
-
# u.is_admin = false
-
# end
-
#
-
# # Creating an Array of new objects using a block, where the block is executed for each object:
-
# User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) do |u|
-
# u.is_admin = false
-
# end
-
1
def create(attributes = nil, options = {}, &block)
-
if attributes.is_a?(Array)
-
attributes.collect { |attr| create(attr, options, &block) }
-
else
-
object = new(attributes, options)
-
yield(object) if block_given?
-
object.save
-
object
-
end
-
end
-
-
# Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
-
# The use of this method should be restricted to complicated SQL queries that can't be executed
-
# using the ActiveRecord::Calculations class methods. Look into those before using this.
-
#
-
# ==== Parameters
-
#
-
# * +sql+ - An SQL statement which should return a count query from the database, see the example below.
-
#
-
# ==== Examples
-
#
-
# Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
-
1
def count_by_sql(sql)
-
sql = sanitize_conditions(sql)
-
connection.select_value(sql, "#{name} Count").to_i
-
end
-
-
# Attributes listed as readonly will be used to create a new record but update operations will
-
# ignore these fields.
-
1
def attr_readonly(*attributes)
-
self._attr_readonly = Set.new(attributes.map { |a| a.to_s }) + (self._attr_readonly || [])
-
end
-
-
# Returns an array of all the attributes that have been specified as readonly.
-
1
def readonly_attributes
-
self._attr_readonly
-
end
-
-
# If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object,
-
# then specify the name of that attribute using this method and it will be handled automatically.
-
# The serialization is done through YAML. If +class_name+ is specified, the serialized object must be of that
-
# class on retrieval or SerializationTypeMismatch will be raised.
-
#
-
# ==== Parameters
-
#
-
# * +attr_name+ - The field name that should be serialized.
-
# * +class_name+ - Optional, class name that the object type should be equal to.
-
#
-
# ==== Example
-
# # Serialize a preferences attribute
-
# class User < ActiveRecord::Base
-
# serialize :preferences
-
# end
-
1
def serialize(attr_name, class_name = Object)
-
coder = if [:load, :dump].all? { |x| class_name.respond_to?(x) }
-
class_name
-
else
-
Coders::YAMLColumn.new(class_name)
-
end
-
-
# merge new serialized attribute and create new hash to ensure that each class in inheritance hierarchy
-
# has its own hash of own serialized attributes
-
self.serialized_attributes = serialized_attributes.merge(attr_name.to_s => coder)
-
end
-
-
# Guesses the table name (in forced lower-case) based on the name of the class in the
-
# inheritance hierarchy descending directly from ActiveRecord::Base. So if the hierarchy
-
# looks like: Reply < Message < ActiveRecord::Base, then Message is used
-
# to guess the table name even when called on Reply. The rules used to do the guess
-
# are handled by the Inflector class in Active Support, which knows almost all common
-
# English inflections. You can add new inflections in config/initializers/inflections.rb.
-
#
-
# Nested classes are given table names prefixed by the singular form of
-
# the parent's table name. Enclosing modules are not considered.
-
#
-
# ==== Examples
-
#
-
# class Invoice < ActiveRecord::Base
-
# end
-
#
-
# file class table_name
-
# invoice.rb Invoice invoices
-
#
-
# class Invoice < ActiveRecord::Base
-
# class Lineitem < ActiveRecord::Base
-
# end
-
# end
-
#
-
# file class table_name
-
# invoice.rb Invoice::Lineitem invoice_lineitems
-
#
-
# module Invoice
-
# class Lineitem < ActiveRecord::Base
-
# end
-
# end
-
#
-
# file class table_name
-
# invoice/lineitem.rb Invoice::Lineitem lineitems
-
#
-
# Additionally, the class-level +table_name_prefix+ is prepended and the
-
# +table_name_suffix+ is appended. So if you have "myapp_" as a prefix,
-
# the table name guess for an Invoice class becomes "myapp_invoices".
-
# Invoice::Lineitem becomes "myapp_invoice_lineitems".
-
#
-
# You can also overwrite this class method to allow for unguessable
-
# links, such as a Mouse class with a link to a "mice" table. Example:
-
#
-
# class Mouse < ActiveRecord::Base
-
# set_table_name "mice"
-
# end
-
1
def table_name
-
2
reset_table_name
-
end
-
-
# Returns a quoted version of the table name, used to construct SQL statements.
-
1
def quoted_table_name
-
@quoted_table_name ||= connection.quote_table_name(table_name)
-
end
-
-
# Computes the table name, (re)sets it internally, and returns it.
-
1
def reset_table_name #:nodoc:
-
2
self.table_name = compute_table_name
-
end
-
-
1
def full_table_name_prefix #:nodoc:
-
4
(parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
-
end
-
-
# Defines the column name for use with single table inheritance. Use
-
# <tt>set_inheritance_column</tt> to set a different value.
-
1
def inheritance_column
-
1
@inheritance_column ||= "type"
-
end
-
-
# Lazy-set the sequence name to the connection's default. This method
-
# is only ever called once since set_sequence_name overrides it.
-
1
def sequence_name #:nodoc:
-
reset_sequence_name
-
end
-
-
1
def reset_sequence_name #:nodoc:
-
default = connection.default_sequence_name(table_name, primary_key)
-
set_sequence_name(default)
-
default
-
end
-
-
# Sets the table name. If the value is nil or false then the value returned by the given
-
# block is used.
-
#
-
# class Project < ActiveRecord::Base
-
# set_table_name "project"
-
# end
-
1
def set_table_name(value = nil, &block)
-
2
@quoted_table_name = nil
-
2
define_attr_method :table_name, value, &block
-
2
@arel_table = nil
-
-
2
@arel_table = Arel::Table.new(table_name, arel_engine)
-
2
@relation = Relation.new(self, arel_table)
-
end
-
1
alias :table_name= :set_table_name
-
-
# Sets the name of the inheritance column to use to the given value,
-
# or (if the value # is nil or false) to the value returned by the
-
# given block.
-
#
-
# class Project < ActiveRecord::Base
-
# set_inheritance_column do
-
# original_inheritance_column + "_id"
-
# end
-
# end
-
1
def set_inheritance_column(value = nil, &block)
-
define_attr_method :inheritance_column, value, &block
-
end
-
1
alias :inheritance_column= :set_inheritance_column
-
-
# Sets the name of the sequence to use when generating ids to the given
-
# value, or (if the value is nil or false) to the value returned by the
-
# given block. This is required for Oracle and is useful for any
-
# database which relies on sequences for primary key generation.
-
#
-
# If a sequence name is not explicitly set when using Oracle or Firebird,
-
# it will default to the commonly used pattern of: #{table_name}_seq
-
#
-
# If a sequence name is not explicitly set when using PostgreSQL, it
-
# will discover the sequence corresponding to your primary key for you.
-
#
-
# class Project < ActiveRecord::Base
-
# set_sequence_name "projectseq" # default would have been "project_seq"
-
# end
-
1
def set_sequence_name(value = nil, &block)
-
define_attr_method :sequence_name, value, &block
-
end
-
1
alias :sequence_name= :set_sequence_name
-
-
# Indicates whether the table associated with this class exists
-
1
def table_exists?
-
2
connection.table_exists?(table_name)
-
end
-
-
# Returns an array of column objects for the table associated with this class.
-
1
def columns
-
3
connection_pool.columns[table_name]
-
end
-
-
# Returns a hash of column objects for the table associated with this class.
-
1
def columns_hash
-
37
connection_pool.columns_hash[table_name]
-
end
-
-
# Returns a hash where the keys are column names and the values are
-
# default values when instantiating the AR object for this table.
-
1
def column_defaults
-
3
connection_pool.column_defaults[table_name]
-
end
-
-
# Returns an array of column names as strings.
-
1
def column_names
-
17
@column_names ||= columns.map { |column| column.name }
-
end
-
-
# Returns an array of column objects where the primary id, all columns ending in "_id" or "_count",
-
# and columns used for single table inheritance have been removed.
-
1
def content_columns
-
@content_columns ||= columns.reject { |c| c.primary || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
-
end
-
-
# Returns a hash of all the methods added to query each of the columns in the table with the name of the method as the key
-
# and true as the value. This makes it possible to do O(1) lookups in respond_to? to check if a given method for attribute
-
# is available.
-
1
def column_methods_hash #:nodoc:
-
@dynamic_methods_hash ||= column_names.inject(Hash.new(false)) do |methods, attr|
-
attr_name = attr.to_s
-
methods[attr.to_sym] = attr_name
-
methods["#{attr}=".to_sym] = attr_name
-
methods["#{attr}?".to_sym] = attr_name
-
methods["#{attr}_before_type_cast".to_sym] = attr_name
-
methods
-
end
-
end
-
-
# Resets all the cached information about columns, which will cause them
-
# to be reloaded on the next request.
-
#
-
# The most common usage pattern for this method is probably in a migration,
-
# when just after creating a table you want to populate it with some default
-
# values, eg:
-
#
-
# class CreateJobLevels < ActiveRecord::Migration
-
# def self.up
-
# create_table :job_levels do |t|
-
# t.integer :id
-
# t.string :name
-
#
-
# t.timestamps
-
# end
-
#
-
# JobLevel.reset_column_information
-
# %w{assistant executive manager director}.each do |type|
-
# JobLevel.create(:name => type)
-
# end
-
# end
-
#
-
# def self.down
-
# drop_table :job_levels
-
# end
-
# end
-
1
def reset_column_information
-
connection.clear_cache!
-
undefine_attribute_methods
-
connection_pool.clear_table_cache!(table_name) if table_exists?
-
-
@column_names = @content_columns = @dynamic_methods_hash = @inheritance_column = nil
-
@arel_engine = @relation = nil
-
end
-
-
1
def clear_cache! # :nodoc:
-
connection_pool.clear_cache!
-
end
-
-
1
def attribute_method?(attribute)
-
super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, '')))
-
end
-
-
# Returns an array of column names as strings if it's not
-
# an abstract class and table exists.
-
# Otherwise it returns an empty array.
-
1
def attribute_names
-
@attribute_names ||= if !abstract_class? && table_exists?
-
2
column_names
-
else
-
[]
-
4
end
-
end
-
-
# Set the lookup ancestors for ActiveModel.
-
1
def lookup_ancestors #:nodoc:
-
klass = self
-
classes = [klass]
-
return classes if klass == ActiveRecord::Base
-
-
while klass != klass.base_class
-
classes << klass = klass.superclass
-
end
-
classes
-
end
-
-
# Set the i18n scope to overwrite ActiveModel.
-
1
def i18n_scope #:nodoc:
-
:activerecord
-
end
-
-
# True if this isn't a concrete subclass needing a STI type condition.
-
1
def descends_from_active_record?
-
1
if superclass.abstract_class?
-
superclass.descends_from_active_record?
-
else
-
1
superclass == Base || !columns_hash.include?(inheritance_column)
-
end
-
end
-
-
1
def finder_needs_type_condition? #:nodoc:
-
# This is like this because benchmarking justifies the strange :false stuff
-
3
:true == (@finder_needs_type_condition ||= descends_from_active_record? ? :false : :true)
-
end
-
-
# Returns a string like 'Post(id:integer, title:string, body:text)'
-
1
def inspect
-
3
if self == Base
-
3
super
-
elsif abstract_class?
-
"#{super}(abstract)"
-
elsif table_exists?
-
attr_list = columns.map { |c| "#{c.name}: #{c.type}" } * ', '
-
"#{super}(#{attr_list})"
-
else
-
"#{super}(Table doesn't exist)"
-
end
-
end
-
-
1
def quote_value(value, column = nil) #:nodoc:
-
connection.quote(value,column)
-
end
-
-
# Used to sanitize objects before they're used in an SQL SELECT statement. Delegates to <tt>connection.quote</tt>.
-
1
def sanitize(object) #:nodoc:
-
connection.quote(object)
-
end
-
-
# Overwrite the default class equality method to provide support for association proxies.
-
1
def ===(object)
-
object.is_a?(self)
-
end
-
-
1
def symbolized_base_class
-
@symbolized_base_class ||= base_class.to_s.to_sym
-
end
-
-
1
def symbolized_sti_name
-
@symbolized_sti_name ||= sti_name.present? ? sti_name.to_sym : symbolized_base_class
-
end
-
-
# Returns the base AR subclass that this class descends from. If A
-
# extends AR::Base, A.base_class will return A. If B descends from A
-
# through some arbitrarily deep hierarchy, B.base_class will return A.
-
#
-
# If B < A and C < B and if A is an abstract_class then both B.base_class
-
# and C.base_class would return B as the answer since A is an abstract_class.
-
1
def base_class
-
4
class_of_active_record_descendant(self)
-
end
-
-
# Set this to true if this is an abstract class (see <tt>abstract_class?</tt>).
-
1
attr_accessor :abstract_class
-
-
# Returns whether this class is an abstract class or not.
-
1
def abstract_class?
-
3
defined?(@abstract_class) && @abstract_class == true
-
end
-
-
1
def respond_to?(method_id, include_private = false)
-
76
if match = DynamicFinderMatch.match(method_id)
-
return true if all_attributes_exists?(match.attribute_names)
-
elsif match = DynamicScopeMatch.match(method_id)
-
return true if all_attributes_exists?(match.attribute_names)
-
end
-
-
76
super
-
end
-
-
1
def sti_name
-
store_full_sti_class ? name : name.demodulize
-
end
-
-
1
def arel_table
-
2
@arel_table ||= Arel::Table.new(table_name, arel_engine)
-
end
-
-
1
def arel_engine
-
@arel_engine ||= begin
-
3
if self == ActiveRecord::Base
-
1
ActiveRecord::Base
-
else
-
2
connection_handler.connection_pools[name] ? self : superclass.arel_engine
-
end
-
4
end
-
end
-
-
# Returns a scope for this class without taking into account the default_scope.
-
#
-
# class Post < ActiveRecord::Base
-
# def self.default_scope
-
# where :published => true
-
# end
-
# end
-
#
-
# Post.all # Fires "SELECT * FROM posts WHERE published = true"
-
# Post.unscoped.all # Fires "SELECT * FROM posts"
-
#
-
# This method also accepts a block meaning that all queries inside the block will
-
# not use the default_scope:
-
#
-
# Post.unscoped {
-
# Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
-
# }
-
#
-
# It is recommended to use block form of unscoped because chaining unscoped with <tt>scope</tt>
-
# does not work. Assuming that <tt>published</tt> is a <tt>scope</tt> following two statements are same.
-
#
-
# Post.unscoped.published
-
# Post.published
-
1
def unscoped #:nodoc:
-
block_given? ? relation.scoping { yield } : relation
-
end
-
-
1
def before_remove_const #:nodoc:
-
self.current_scope = nil
-
end
-
-
# Finder methods must instantiate through this method to work with the
-
# single-table inheritance model that makes it possible to create
-
# objects of different types from the same table.
-
1
def instantiate(record)
-
sti_class = find_sti_class(record[inheritance_column])
-
record_id = sti_class.primary_key && record[sti_class.primary_key]
-
-
if ActiveRecord::IdentityMap.enabled? && record_id
-
if (column = sti_class.columns_hash[sti_class.primary_key]) && column.number?
-
record_id = record_id.to_i
-
end
-
if instance = IdentityMap.get(sti_class, record_id)
-
instance.reinit_with('attributes' => record)
-
else
-
instance = sti_class.allocate.init_with('attributes' => record)
-
IdentityMap.add(instance)
-
end
-
else
-
instance = sti_class.allocate.init_with('attributes' => record)
-
end
-
-
instance
-
end
-
-
1
private
-
-
1
def relation #:nodoc:
-
@relation ||= Relation.new(self, arel_table)
-
-
if finder_needs_type_condition?
-
@relation.where(type_condition).create_with(inheritance_column.to_sym => sti_name)
-
else
-
@relation
-
end
-
end
-
-
1
def find_sti_class(type_name)
-
if type_name.blank? || !columns_hash.include?(inheritance_column)
-
self
-
else
-
begin
-
if store_full_sti_class
-
ActiveSupport::Dependencies.constantize(type_name)
-
else
-
compute_type(type_name)
-
end
-
rescue NameError
-
raise SubclassNotFound,
-
"The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " +
-
"This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
-
"Please rename this column if you didn't intend it to be used for storing the inheritance class " +
-
"or overwrite #{name}.inheritance_column to use another column for that information."
-
end
-
end
-
end
-
-
1
def construct_finder_arel(options = {}, scope = nil)
-
relation = options.is_a?(Hash) ? unscoped.apply_finder_options(options) : options
-
relation = scope.merge(relation) if scope
-
relation
-
end
-
-
1
def type_condition(table = arel_table)
-
sti_column = table[inheritance_column.to_sym]
-
sti_names = ([self] + descendants).map { |model| model.sti_name }
-
-
sti_column.in(sti_names)
-
end
-
-
# Guesses the table name, but does not decorate it with prefix and suffix information.
-
1
def undecorated_table_name(class_name = base_class.name)
-
2
table_name = class_name.to_s.demodulize.underscore
-
2
table_name = table_name.pluralize if pluralize_table_names
-
2
table_name
-
end
-
-
# Computes and returns a table name according to default conventions.
-
1
def compute_table_name
-
2
base = base_class
-
2
if self == base
-
# Nested classes are prefixed with singular parent table name.
-
2
if parent < ActiveRecord::Base && !parent.abstract_class?
-
contained = parent.table_name
-
contained = contained.singularize if parent.pluralize_table_names
-
contained += '_'
-
end
-
2
"#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{table_name_suffix}"
-
else
-
# STI subclasses always use their superclass' table.
-
base.table_name
-
end
-
end
-
-
# Enables dynamic finders like <tt>User.find_by_user_name(user_name)</tt> and
-
# <tt>User.scoped_by_user_name(user_name). Refer to Dynamic attribute-based finders
-
# section at the top of this file for more detailed information.
-
#
-
# It's even possible to use all the additional parameters to +find+. For example, the
-
# full interface for +find_all_by_amount+ is actually <tt>find_all_by_amount(amount, options)</tt>.
-
#
-
# Each dynamic finder using <tt>scoped_by_*</tt> is also defined in the class after it
-
# is first invoked, so that future attempts to use it do not run through method_missing.
-
1
def method_missing(method_id, *arguments, &block)
-
if match = DynamicFinderMatch.match(method_id)
-
attribute_names = match.attribute_names
-
super unless all_attributes_exists?(attribute_names)
-
if !arguments.first.is_a?(Hash) && arguments.size < attribute_names.size
-
ActiveSupport::Deprecation.warn(<<-eowarn)
-
Calling dynamic finder with less number of arguments than the number of attributes in method name is deprecated and will raise an ArguementError in the next version of Rails. Please passing `nil' to the argument you want it to be nil.
-
eowarn
-
end
-
if match.finder?
-
options = arguments.extract_options!
-
relation = options.any? ? scoped(options) : scoped
-
relation.send :find_by_attributes, match, attribute_names, *arguments
-
elsif match.instantiator?
-
scoped.send :find_or_instantiator_by_attributes, match, attribute_names, *arguments, &block
-
end
-
elsif match = DynamicScopeMatch.match(method_id)
-
attribute_names = match.attribute_names
-
super unless all_attributes_exists?(attribute_names)
-
if arguments.size < attribute_names.size
-
ActiveSupport::Deprecation.warn(
-
"Calling dynamic scope with less number of arguments than the number of attributes in " \
-
"method name is deprecated and will raise an ArguementError in the next version of Rails. " \
-
"Please passing `nil' to the argument you want it to be nil."
-
)
-
end
-
if match.scope?
-
self.class_eval <<-METHOD, __FILE__, __LINE__ + 1
-
def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args)
-
attributes = Hash[[:#{attribute_names.join(',:')}].zip(args)] # attributes = Hash[[:user_name, :password].zip(args)]
-
#
-
scoped(:conditions => attributes) # scoped(:conditions => attributes)
-
end # end
-
METHOD
-
send(method_id, *arguments)
-
end
-
else
-
super
-
end
-
end
-
-
# Similar in purpose to +expand_hash_conditions_for_aggregates+.
-
1
def expand_attribute_names_for_aggregates(attribute_names)
-
attribute_names.map { |attribute_name|
-
unless (aggregation = reflect_on_aggregation(attribute_name.to_sym)).nil?
-
aggregate_mapping(aggregation).map do |field_attr, _|
-
field_attr.to_sym
-
end
-
else
-
attribute_name.to_sym
-
end
-
}.flatten
-
end
-
-
1
def all_attributes_exists?(attribute_names)
-
(expand_attribute_names_for_aggregates(attribute_names) -
-
column_methods_hash.keys).empty?
-
end
-
-
1
protected
-
# with_scope lets you apply options to inner block incrementally. It takes a hash and the keys must be
-
# <tt>:find</tt> or <tt>:create</tt>. <tt>:find</tt> parameter is <tt>Relation</tt> while
-
# <tt>:create</tt> parameters are an attributes hash.
-
#
-
# class Article < ActiveRecord::Base
-
# def self.create_with_scope
-
# with_scope(:find => where(:blog_id => 1), :create => { :blog_id => 1 }) do
-
# find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1
-
# a = create(1)
-
# a.blog_id # => 1
-
# end
-
# end
-
# end
-
#
-
# In nested scopings, all previous parameters are overwritten by the innermost rule, with the exception of
-
# <tt>where</tt>, <tt>includes</tt>, and <tt>joins</tt> operations in <tt>Relation</tt>, which are merged.
-
#
-
# <tt>joins</tt> operations are uniqued so multiple scopes can join in the same table without table aliasing
-
# problems. If you need to join multiple tables, but still want one of the tables to be uniqued, use the
-
# array of strings format for your joins.
-
#
-
# class Article < ActiveRecord::Base
-
# def self.find_with_scope
-
# with_scope(:find => where(:blog_id => 1).limit(1), :create => { :blog_id => 1 }) do
-
# with_scope(:find => limit(10)) do
-
# all # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
-
# end
-
# with_scope(:find => where(:author_id => 3)) do
-
# all # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
-
# end
-
# end
-
# end
-
# end
-
#
-
# You can ignore any previous scopings by using the <tt>with_exclusive_scope</tt> method.
-
#
-
# class Article < ActiveRecord::Base
-
# def self.find_with_exclusive_scope
-
# with_scope(:find => where(:blog_id => 1).limit(1)) do
-
# with_exclusive_scope(:find => limit(10)) do
-
# all # => SELECT * from articles LIMIT 10
-
# end
-
# end
-
# end
-
# end
-
#
-
# *Note*: the +:find+ scope also has effect on update and deletion methods, like +update_all+ and +delete_all+.
-
1
def with_scope(scope = {}, action = :merge, &block)
-
# If another Active Record class has been passed in, get its current scope
-
scope = scope.current_scope if !scope.is_a?(Relation) && scope.respond_to?(:current_scope)
-
-
previous_scope = self.current_scope
-
-
if scope.is_a?(Hash)
-
# Dup first and second level of hash (method and params).
-
scope = scope.dup
-
scope.each do |method, params|
-
scope[method] = params.dup unless params == true
-
end
-
-
scope.assert_valid_keys([ :find, :create ])
-
relation = construct_finder_arel(scope[:find] || {})
-
relation.default_scoped = true unless action == :overwrite
-
-
if previous_scope && previous_scope.create_with_value && scope[:create]
-
scope_for_create = if action == :merge
-
previous_scope.create_with_value.merge(scope[:create])
-
else
-
scope[:create]
-
end
-
-
relation = relation.create_with(scope_for_create)
-
else
-
scope_for_create = scope[:create]
-
scope_for_create ||= previous_scope.create_with_value if previous_scope
-
relation = relation.create_with(scope_for_create) if scope_for_create
-
end
-
-
scope = relation
-
end
-
-
scope = previous_scope.merge(scope) if previous_scope && action == :merge
-
-
self.current_scope = scope
-
begin
-
yield
-
ensure
-
self.current_scope = previous_scope
-
end
-
end
-
-
# Works like with_scope, but discards any nested properties.
-
1
def with_exclusive_scope(method_scoping = {}, &block)
-
if method_scoping.values.any? { |e| e.is_a?(ActiveRecord::Relation) }
-
raise ArgumentError, <<-MSG
-
New finder API can not be used with_exclusive_scope. You can either call unscoped to get an anonymous scope not bound to the default_scope:
-
-
User.unscoped.where(:active => true)
-
-
Or call unscoped with a block:
-
-
User.unscoped do
-
User.where(:active => true).all
-
end
-
-
MSG
-
end
-
with_scope(method_scoping, :overwrite, &block)
-
end
-
-
1
def current_scope #:nodoc:
-
3
Thread.current["#{self}_current_scope"]
-
end
-
-
1
def current_scope=(scope) #:nodoc:
-
Thread.current["#{self}_current_scope"] = scope
-
end
-
-
# Use this macro in your model to set a default scope for all operations on
-
# the model.
-
#
-
# class Article < ActiveRecord::Base
-
# default_scope where(:published => true)
-
# end
-
#
-
# Article.all # => SELECT * FROM articles WHERE published = true
-
#
-
# The <tt>default_scope</tt> is also applied while creating/building a record. It is not
-
# applied while updating a record.
-
#
-
# Article.new.published # => true
-
# Article.create.published # => true
-
#
-
# You can also use <tt>default_scope</tt> with a block, in order to have it lazily evaluated:
-
#
-
# class Article < ActiveRecord::Base
-
# default_scope { where(:published_at => Time.now - 1.week) }
-
# end
-
#
-
# (You can also pass any object which responds to <tt>call</tt> to the <tt>default_scope</tt>
-
# macro, and it will be called when building the default scope.)
-
#
-
# If you use multiple <tt>default_scope</tt> declarations in your model then they will
-
# be merged together:
-
#
-
# class Article < ActiveRecord::Base
-
# default_scope where(:published => true)
-
# default_scope where(:rating => 'G')
-
# end
-
#
-
# Article.all # => SELECT * FROM articles WHERE published = true AND rating = 'G'
-
#
-
# This is also the case with inheritance and module includes where the parent or module
-
# defines a <tt>default_scope</tt> and the child or including class defines a second one.
-
#
-
# If you need to do more complex things with a default scope, you can alternatively
-
# define it as a class method:
-
#
-
# class Article < ActiveRecord::Base
-
# def self.default_scope
-
# # Should return a scope, you can call 'super' here etc.
-
# end
-
# end
-
1
def default_scope(scope = {})
-
scope = Proc.new if block_given?
-
self.default_scopes = default_scopes + [scope]
-
end
-
-
1
def build_default_scope #:nodoc:
-
if method(:default_scope).owner != Base.singleton_class
-
evaluate_default_scope { default_scope }
-
elsif default_scopes.any?
-
evaluate_default_scope do
-
default_scopes.inject(relation) do |default_scope, scope|
-
if scope.is_a?(Hash)
-
default_scope.apply_finder_options(scope)
-
elsif !scope.is_a?(Relation) && scope.respond_to?(:call)
-
default_scope.merge(scope.call)
-
else
-
default_scope.merge(scope)
-
end
-
end
-
end
-
end
-
end
-
-
1
def ignore_default_scope? #:nodoc:
-
Thread.current["#{self}_ignore_default_scope"]
-
end
-
-
1
def ignore_default_scope=(ignore) #:nodoc:
-
Thread.current["#{self}_ignore_default_scope"] = ignore
-
end
-
-
# The ignore_default_scope flag is used to prevent an infinite recursion situation where
-
# a default scope references a scope which has a default scope which references a scope...
-
1
def evaluate_default_scope
-
return if ignore_default_scope?
-
-
begin
-
self.ignore_default_scope = true
-
yield
-
ensure
-
self.ignore_default_scope = false
-
end
-
end
-
-
# Returns the class type of the record using the current module as a prefix. So descendants of
-
# MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
-
1
def compute_type(type_name)
-
if type_name.match(/^::/)
-
# If the type is prefixed with a scope operator then we assume that
-
# the type_name is an absolute reference.
-
ActiveSupport::Dependencies.constantize(type_name)
-
else
-
# Build a list of candidates to search for
-
candidates = []
-
name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" }
-
candidates << type_name
-
-
candidates.each do |candidate|
-
begin
-
constant = ActiveSupport::Dependencies.constantize(candidate)
-
return constant if candidate == constant.to_s
-
rescue NameError => e
-
# We don't want to swallow NoMethodError < NameError errors
-
raise e unless e.instance_of?(NameError)
-
end
-
end
-
-
raise NameError, "uninitialized constant #{candidates.first}"
-
end
-
end
-
-
# Returns the class descending directly from ActiveRecord::Base or an
-
# abstract class, if any, in the inheritance hierarchy.
-
1
def class_of_active_record_descendant(klass)
-
4
if klass.superclass == Base || klass.superclass.abstract_class?
-
4
klass
-
elsif klass.superclass.nil?
-
raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
-
else
-
class_of_active_record_descendant(klass.superclass)
-
end
-
end
-
-
# Accepts an array, hash, or string of SQL conditions and sanitizes
-
# them into a valid SQL fragment for a WHERE clause.
-
# ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
-
# { :name => "foo'bar", :group_id => 4 } returns "name='foo''bar' and group_id='4'"
-
# "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
-
1
def sanitize_sql_for_conditions(condition, table_name = self.table_name)
-
return nil if condition.blank?
-
-
case condition
-
when Array; sanitize_sql_array(condition)
-
when Hash; sanitize_sql_hash_for_conditions(condition, table_name)
-
else condition
-
end
-
end
-
1
alias_method :sanitize_sql, :sanitize_sql_for_conditions
-
-
# Accepts an array, hash, or string of SQL conditions and sanitizes
-
# them into a valid SQL fragment for a SET clause.
-
# { :name => nil, :group_id => 4 } returns "name = NULL , group_id='4'"
-
1
def sanitize_sql_for_assignment(assignments)
-
case assignments
-
when Array; sanitize_sql_array(assignments)
-
when Hash; sanitize_sql_hash_for_assignment(assignments)
-
else assignments
-
end
-
end
-
-
1
def aggregate_mapping(reflection)
-
mapping = reflection.options[:mapping] || [reflection.name, reflection.name]
-
mapping.first.is_a?(Array) ? mapping : [mapping]
-
end
-
-
# Accepts a hash of SQL conditions and replaces those attributes
-
# that correspond to a +composed_of+ relationship with their expanded
-
# aggregate attribute values.
-
# Given:
-
# class Person < ActiveRecord::Base
-
# composed_of :address, :class_name => "Address",
-
# :mapping => [%w(address_street street), %w(address_city city)]
-
# end
-
# Then:
-
# { :address => Address.new("813 abc st.", "chicago") }
-
# # => { :address_street => "813 abc st.", :address_city => "chicago" }
-
1
def expand_hash_conditions_for_aggregates(attrs)
-
expanded_attrs = {}
-
attrs.each do |attr, value|
-
unless (aggregation = reflect_on_aggregation(attr.to_sym)).nil?
-
mapping = aggregate_mapping(aggregation)
-
mapping.each do |field_attr, aggregate_attr|
-
if mapping.size == 1 && !value.respond_to?(aggregate_attr)
-
expanded_attrs[field_attr] = value
-
else
-
expanded_attrs[field_attr] = value.send(aggregate_attr)
-
end
-
end
-
else
-
expanded_attrs[attr] = value
-
end
-
end
-
expanded_attrs
-
end
-
-
# Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause.
-
# { :name => "foo'bar", :group_id => 4 }
-
# # => "name='foo''bar' and group_id= 4"
-
# { :status => nil, :group_id => [1,2,3] }
-
# # => "status IS NULL and group_id IN (1,2,3)"
-
# { :age => 13..18 }
-
# # => "age BETWEEN 13 AND 18"
-
# { 'other_records.id' => 7 }
-
# # => "`other_records`.`id` = 7"
-
# { :other_records => { :id => 7 } }
-
# # => "`other_records`.`id` = 7"
-
# And for value objects on a composed_of relationship:
-
# { :address => Address.new("123 abc st.", "chicago") }
-
# # => "address_street='123 abc st.' and address_city='chicago'"
-
1
def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name)
-
attrs = expand_hash_conditions_for_aggregates(attrs)
-
-
table = Arel::Table.new(table_name).alias(default_table_name)
-
PredicateBuilder.build_from_hash(arel_engine, attrs, table).map { |b|
-
connection.visitor.accept b
-
}.join(' AND ')
-
end
-
1
alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
-
-
# Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
-
# { :status => nil, :group_id => 1 }
-
# # => "status = NULL , group_id = 1"
-
1
def sanitize_sql_hash_for_assignment(attrs)
-
attrs.map do |attr, value|
-
"#{connection.quote_column_name(attr)} = #{quote_bound_value(value)}"
-
end.join(', ')
-
end
-
-
# Accepts an array of conditions. The array has each value
-
# sanitized and interpolated into the SQL statement.
-
# ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
-
1
def sanitize_sql_array(ary)
-
statement, *values = ary
-
if values.first.is_a?(Hash) && statement =~ /:\w+/
-
replace_named_bind_variables(statement, values.first)
-
elsif statement.include?('?')
-
replace_bind_variables(statement, values)
-
elsif statement.blank?
-
statement
-
else
-
statement % values.collect { |value| connection.quote_string(value.to_s) }
-
end
-
end
-
-
1
alias_method :sanitize_conditions, :sanitize_sql
-
-
1
def replace_bind_variables(statement, values) #:nodoc:
-
raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
-
bound = values.dup
-
c = connection
-
statement.gsub('?') { quote_bound_value(bound.shift, c) }
-
end
-
-
1
def replace_named_bind_variables(statement, bind_vars) #:nodoc:
-
statement.gsub(/(:?):([a-zA-Z]\w*)/) do
-
if $1 == ':' # skip postgresql casts
-
$& # return the whole match
-
elsif bind_vars.include?(match = $2.to_sym)
-
quote_bound_value(bind_vars[match])
-
else
-
raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
-
end
-
end
-
end
-
-
1
def expand_range_bind_variables(bind_vars) #:nodoc:
-
expanded = []
-
-
bind_vars.each do |var|
-
next if var.is_a?(Hash)
-
-
if var.is_a?(Range)
-
expanded << var.first
-
expanded << var.last
-
else
-
expanded << var
-
end
-
end
-
-
expanded
-
end
-
-
1
def quote_bound_value(value, c = connection) #:nodoc:
-
if value.respond_to?(:map) && !value.acts_like?(:string)
-
if value.respond_to?(:empty?) && value.empty?
-
c.quote(nil)
-
else
-
value.map { |v| c.quote(v) }.join(',')
-
end
-
else
-
c.quote(value)
-
end
-
end
-
-
1
def raise_if_bind_arity_mismatch(statement, expected, provided) #:nodoc:
-
unless expected == provided
-
raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
-
end
-
end
-
-
1
def encode_quoted_value(value) #:nodoc:
-
quoted_value = connection.quote(value)
-
quoted_value = "'#{quoted_value[1..-2].gsub(/\'/, "\\\\'")}'" if quoted_value.include?("\\\'") # (for ruby mode) "
-
quoted_value
-
end
-
end
-
-
1
public
-
# New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
-
# attributes but not yet saved (pass a hash with key names matching the associated table column names).
-
# In both instances, valid attribute keys are determined by the column names of the associated table --
-
# hence you can't have attributes that aren't part of the table columns.
-
#
-
# +initialize+ respects mass-assignment security and accepts either +:as+ or +:without_protection+ options
-
# in the +options+ parameter.
-
#
-
# ==== Examples
-
# # Instantiates a single new object
-
# User.new(:first_name => 'Jamie')
-
#
-
# # Instantiates a single new object using the :admin mass-assignment security role
-
# User.new({ :first_name => 'Jamie', :is_admin => true }, :as => :admin)
-
#
-
# # Instantiates a single new object bypassing mass-assignment security
-
# User.new({ :first_name => 'Jamie', :is_admin => true }, :without_protection => true)
-
1
def initialize(attributes = nil, options = {})
-
3
@attributes = attributes_from_column_definition
-
3
@association_cache = {}
-
3
@aggregation_cache = {}
-
3
@attributes_cache = {}
-
3
@new_record = true
-
3
@readonly = false
-
3
@destroyed = false
-
3
@marked_for_destruction = false
-
3
@previously_changed = {}
-
3
@changed_attributes = {}
-
3
@relation = nil
-
-
3
ensure_proper_type
-
3
set_serialized_attributes
-
-
3
populate_with_current_scope_attributes
-
-
3
assign_attributes(attributes, options) if attributes
-
-
3
yield self if block_given?
-
3
run_callbacks :initialize
-
end
-
-
# Populate +coder+ with attributes about this record that should be
-
# serialized. The structure of +coder+ defined in this method is
-
# guaranteed to match the structure of +coder+ passed to the +init_with+
-
# method.
-
#
-
# Example:
-
#
-
# class Post < ActiveRecord::Base
-
# end
-
# coder = {}
-
# Post.new.encode_with(coder)
-
# coder # => { 'id' => nil, ... }
-
1
def encode_with(coder)
-
coder['attributes'] = attributes
-
end
-
-
# Initialize an empty model object from +coder+. +coder+ must contain
-
# the attributes necessary for initializing an empty model object. For
-
# example:
-
#
-
# class Post < ActiveRecord::Base
-
# end
-
#
-
# post = Post.allocate
-
# post.init_with('attributes' => { 'title' => 'hello world' })
-
# post.title # => 'hello world'
-
1
def init_with(coder)
-
@attributes = coder['attributes']
-
@relation = nil
-
-
set_serialized_attributes
-
-
@attributes_cache, @previously_changed, @changed_attributes = {}, {}, {}
-
@association_cache = {}
-
@aggregation_cache = {}
-
@readonly = @destroyed = @marked_for_destruction = false
-
@new_record = false
-
run_callbacks :find
-
run_callbacks :initialize
-
-
self
-
end
-
-
# Returns a String, which Action Pack uses for constructing an URL to this
-
# object. The default implementation returns this record's id as a String,
-
# or nil if this record's unsaved.
-
#
-
# For example, suppose that you have a User model, and that you have a
-
# <tt>resources :users</tt> route. Normally, +user_path+ will
-
# construct a path with the user object's 'id' in it:
-
#
-
# user = User.find_by_name('Phusion')
-
# user_path(user) # => "/users/1"
-
#
-
# You can override +to_param+ in your model to make +user_path+ construct
-
# a path using the user's name instead of the user's id:
-
#
-
# class User < ActiveRecord::Base
-
# def to_param # overridden
-
# name
-
# end
-
# end
-
#
-
# user = User.find_by_name('Phusion')
-
# user_path(user) # => "/users/Phusion"
-
1
def to_param
-
# We can't use alias_method here, because method 'id' optimizes itself on the fly.
-
id && id.to_s # Be sure to stringify the id for routes
-
end
-
-
# Returns a cache key that can be used to identify this record.
-
#
-
# ==== Examples
-
#
-
# Product.new.cache_key # => "products/new"
-
# Product.find(5).cache_key # => "products/5" (updated_at not available)
-
# Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
-
1
def cache_key
-
case
-
when new_record?
-
"#{self.class.model_name.cache_key}/new"
-
when timestamp = self[:updated_at]
-
timestamp = timestamp.utc.to_s(:number)
-
"#{self.class.model_name.cache_key}/#{id}-#{timestamp}"
-
else
-
"#{self.class.model_name.cache_key}/#{id}"
-
end
-
end
-
-
1
def quoted_id #:nodoc:
-
quote_value(id, column_for_attribute(self.class.primary_key))
-
end
-
-
# Returns true if the given attribute is in the attributes hash
-
1
def has_attribute?(attr_name)
-
@attributes.has_key?(attr_name.to_s)
-
end
-
-
# Returns an array of names for the attributes available on this object.
-
1
def attribute_names
-
@attributes.keys
-
end
-
-
# Allows you to set all the attributes at once by passing in a hash with keys
-
# matching the attribute names (which again matches the column names).
-
#
-
# If any attributes are protected by either +attr_protected+ or
-
# +attr_accessible+ then only settable attributes will be assigned.
-
#
-
# The +guard_protected_attributes+ argument is now deprecated, use
-
# the +assign_attributes+ method if you want to bypass mass-assignment security.
-
#
-
# class User < ActiveRecord::Base
-
# attr_protected :is_admin
-
# end
-
#
-
# user = User.new
-
# user.attributes = { :username => 'Phusion', :is_admin => true }
-
# user.username # => "Phusion"
-
# user.is_admin? # => false
-
1
def attributes=(new_attributes, guard_protected_attributes = nil)
-
unless guard_protected_attributes.nil?
-
message = "the use of 'guard_protected_attributes' will be removed from the next minor release of rails, " +
-
"if you want to bypass mass-assignment security then look into using assign_attributes"
-
ActiveSupport::Deprecation.warn(message)
-
end
-
-
return unless new_attributes.is_a?(Hash)
-
-
if guard_protected_attributes == false
-
assign_attributes(new_attributes, :without_protection => true)
-
else
-
assign_attributes(new_attributes)
-
end
-
end
-
-
# Allows you to set all the attributes for a particular mass-assignment
-
# security role by passing in a hash of attributes with keys matching
-
# the attribute names (which again matches the column names) and the role
-
# name using the :as option.
-
#
-
# To bypass mass-assignment security you can use the :without_protection => true
-
# option.
-
#
-
# class User < ActiveRecord::Base
-
# attr_accessible :name
-
# attr_accessible :name, :is_admin, :as => :admin
-
# end
-
#
-
# user = User.new
-
# user.assign_attributes({ :name => 'Josh', :is_admin => true })
-
# user.name # => "Josh"
-
# user.is_admin? # => false
-
#
-
# user = User.new
-
# user.assign_attributes({ :name => 'Josh', :is_admin => true }, :as => :admin)
-
# user.name # => "Josh"
-
# user.is_admin? # => true
-
#
-
# user = User.new
-
# user.assign_attributes({ :name => 'Josh', :is_admin => true }, :without_protection => true)
-
# user.name # => "Josh"
-
# user.is_admin? # => true
-
1
def assign_attributes(new_attributes, options = {})
-
3
return unless new_attributes
-
-
3
attributes = new_attributes.stringify_keys
-
3
multi_parameter_attributes = []
-
3
@mass_assignment_options = options
-
-
3
unless options[:without_protection]
-
3
attributes = sanitize_for_mass_assignment(attributes, mass_assignment_role)
-
end
-
-
3
attributes.each do |k, v|
-
9
if k.include?("(")
-
multi_parameter_attributes << [ k, v ]
-
elsif respond_to?("#{k}=")
-
9
send("#{k}=", v)
-
else
-
raise(UnknownAttributeError, "unknown attribute: #{k}")
-
end
-
end
-
-
3
@mass_assignment_options = nil
-
3
assign_multiparameter_attributes(multi_parameter_attributes)
-
end
-
-
# Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
-
1
def attributes
-
Hash[@attributes.map { |name, _| [name, read_attribute(name)] }]
-
end
-
-
# Returns an <tt>#inspect</tt>-like string for the value of the
-
# attribute +attr_name+. String attributes are truncated upto 50
-
# characters, and Date and Time attributes are returned in the
-
# <tt>:db</tt> format. Other attributes return the value of
-
# <tt>#inspect</tt> without modification.
-
#
-
# person = Person.create!(:name => "David Heinemeier Hansson " * 3)
-
#
-
# person.attribute_for_inspect(:name)
-
# # => '"David Heinemeier Hansson David Heinemeier Hansson D..."'
-
#
-
# person.attribute_for_inspect(:created_at)
-
# # => '"2009-01-12 04:48:57"'
-
1
def attribute_for_inspect(attr_name)
-
value = read_attribute(attr_name)
-
-
if value.is_a?(String) && value.length > 50
-
"#{value[0..50]}...".inspect
-
elsif value.is_a?(Date) || value.is_a?(Time)
-
%("#{value.to_s(:db)}")
-
else
-
value.inspect
-
end
-
end
-
-
# Returns true if the specified +attribute+ has been set by the user or by a database load and is neither
-
# nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings).
-
1
def attribute_present?(attribute)
-
!_read_attribute(attribute).blank?
-
end
-
-
# Returns the column object for the named attribute.
-
1
def column_for_attribute(name)
-
18
self.class.columns_hash[name.to_s]
-
end
-
-
# Returns true if +comparison_object+ is the same exact object, or +comparison_object+
-
# is of the same type and +self+ has an ID and it is equal to +comparison_object.id+.
-
#
-
# Note that new records are different from any other record by definition, unless the
-
# other record is the receiver itself. Besides, if you fetch existing records with
-
# +select+ and leave the ID out, you're on your own, this predicate will return false.
-
#
-
# Note also that destroying a record preserves its ID in the model instance, so deleted
-
# models are still comparable.
-
1
def ==(comparison_object)
-
super ||
-
comparison_object.instance_of?(self.class) &&
-
id.present? &&
-
comparison_object.id == id
-
end
-
1
alias :eql? :==
-
-
# Delegates to id in order to allow two records of the same type and id to work with something like:
-
# [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
-
1
def hash
-
id.hash
-
end
-
-
# Freeze the attributes hash such that associations are still accessible, even on destroyed records.
-
1
def freeze
-
@attributes.freeze; self
-
end
-
-
# Returns +true+ if the attributes hash has been frozen.
-
1
def frozen?
-
@attributes.frozen?
-
end
-
-
# Allows sort on objects
-
1
def <=>(other_object)
-
if other_object.is_a?(self.class)
-
self.to_key <=> other_object.to_key
-
else
-
nil
-
end
-
end
-
-
# Backport dup from 1.9 so that initialize_dup() gets called
-
1
unless Object.respond_to?(:initialize_dup)
-
def dup # :nodoc:
-
copy = super
-
copy.initialize_dup(self)
-
copy
-
end
-
end
-
-
# Duped objects have no id assigned and are treated as new records. Note
-
# that this is a "shallow" copy as it copies the object's attributes
-
# only, not its associations. The extent of a "deep" copy is application
-
# specific and is therefore left to the application to implement according
-
# to its need.
-
# The dup method does not preserve the timestamps (created|updated)_(at|on).
-
1
def initialize_dup(other)
-
cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
-
cloned_attributes.delete(self.class.primary_key)
-
-
@attributes = cloned_attributes
-
-
_run_after_initialize_callbacks if respond_to?(:_run_after_initialize_callbacks)
-
-
@changed_attributes = {}
-
attributes_from_column_definition.each do |attr, orig_value|
-
@changed_attributes[attr] = orig_value if field_changed?(attr, orig_value, @attributes[attr])
-
end
-
-
@aggregation_cache = {}
-
@association_cache = {}
-
@attributes_cache = {}
-
@new_record = true
-
-
ensure_proper_type
-
populate_with_current_scope_attributes
-
clear_timestamp_attributes
-
end
-
-
# Returns +true+ if the record is read only. Records loaded through joins with piggy-back
-
# attributes will be marked as read only since they cannot be saved.
-
1
def readonly?
-
@readonly
-
end
-
-
# Marks this record as read only.
-
1
def readonly!
-
@readonly = true
-
end
-
-
# Returns the contents of the record as a nicely formatted string.
-
1
def inspect
-
attributes_as_nice_string = self.class.column_names.collect { |name|
-
if has_attribute?(name)
-
"#{name}: #{attribute_for_inspect(name)}"
-
end
-
}.compact.join(", ")
-
"#<#{self.class} #{attributes_as_nice_string}>"
-
end
-
-
1
protected
-
1
def clone_attributes(reader_method = :read_attribute, attributes = {})
-
attribute_names.each do |name|
-
attributes[name] = clone_attribute_value(reader_method, name)
-
end
-
attributes
-
end
-
-
1
def clone_attribute_value(reader_method, attribute_name)
-
9
value = send(reader_method, attribute_name)
-
9
value.duplicable? ? value.clone : value
-
rescue TypeError, NoMethodError
-
value
-
end
-
-
1
def mass_assignment_options
-
3
@mass_assignment_options ||= {}
-
end
-
-
1
def mass_assignment_role
-
3
mass_assignment_options[:as] || :default
-
end
-
-
1
private
-
-
# Under Ruby 1.9, Array#flatten will call #to_ary (recursively) on each of the elements
-
# of the array, and then rescues from the possible NoMethodError. If those elements are
-
# ActiveRecord::Base's, then this triggers the various method_missing's that we have,
-
# which significantly impacts upon performance.
-
#
-
# So we can avoid the method_missing hit by explicitly defining #to_ary as nil here.
-
#
-
# See also http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary/
-
1
def to_ary # :nodoc:
-
nil
-
end
-
-
1
def set_serialized_attributes
-
3
sattrs = self.class.serialized_attributes
-
-
3
sattrs.each do |key, coder|
-
@attributes[key] = coder.load @attributes[key] if @attributes.key?(key)
-
end
-
end
-
-
# Sets the attribute used for single table inheritance to this class name if this is not the
-
# ActiveRecord::Base descendant.
-
# Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to
-
# do Reply.new without having to set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself.
-
# No such attribute would be set for objects of the Message class in that example.
-
1
def ensure_proper_type
-
3
klass = self.class
-
3
if klass.finder_needs_type_condition?
-
write_attribute(klass.inheritance_column, klass.sti_name)
-
end
-
end
-
-
# The primary key and inheritance column can never be set by mass-assignment for security reasons.
-
1
def self.attributes_protected_by_default
-
1
default = [ primary_key, inheritance_column ]
-
1
default << 'id' unless primary_key.eql? 'id'
-
1
default
-
end
-
-
# Returns a copy of the attributes hash where all the values have been safely quoted for use in
-
# an Arel insert/update method.
-
1
def arel_attributes_values(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys)
-
attrs = {}
-
klass = self.class
-
arel_table = klass.arel_table
-
-
attribute_names.each do |name|
-
if (column = column_for_attribute(name)) && (include_primary_key || !column.primary)
-
-
if include_readonly_attributes || (!include_readonly_attributes && !self.class.readonly_attributes.include?(name))
-
-
value = if coder = klass.serialized_attributes[name]
-
coder.dump @attributes[name]
-
else
-
# FIXME: we need @attributes to be used consistently.
-
# If the values stored in @attributes were already type
-
# casted, this code could be simplified
-
read_attribute(name)
-
end
-
-
attrs[arel_table[name]] = value
-
end
-
end
-
end
-
attrs
-
end
-
-
# Quote strings appropriately for SQL statements.
-
1
def quote_value(value, column = nil)
-
self.class.connection.quote(value, column)
-
end
-
-
# Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
-
# by calling new on the column type or aggregation type (through composed_of) object with these parameters.
-
# So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
-
# written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
-
# parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum,
-
# f for Float, s for String, and a for Array. If all the values for a given attribute are empty, the
-
# attribute will be set to nil.
-
1
def assign_multiparameter_attributes(pairs)
-
execute_callstack_for_multiparameter_attributes(
-
3
extract_callstack_for_multiparameter_attributes(pairs)
-
)
-
end
-
-
1
def instantiate_time_object(name, values)
-
if self.class.send(:create_time_zone_conversion_attribute?, name, column_for_attribute(name))
-
Time.zone.local(*values)
-
else
-
Time.time_with_datetime_fallback(@@default_timezone, *values)
-
end
-
end
-
-
1
def execute_callstack_for_multiparameter_attributes(callstack)
-
3
errors = []
-
3
callstack.each do |name, values_with_empty_parameters|
-
begin
-
send(name + "=", read_value_from_parameter(name, values_with_empty_parameters))
-
rescue => ex
-
errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name}", ex, name)
-
end
-
end
-
3
unless errors.empty?
-
raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes"
-
end
-
end
-
-
1
def read_value_from_parameter(name, values_hash_from_param)
-
klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
-
if values_hash_from_param.values.all?{|v|v.nil?}
-
nil
-
elsif klass == Time
-
read_time_parameter_value(name, values_hash_from_param)
-
elsif klass == Date
-
read_date_parameter_value(name, values_hash_from_param)
-
else
-
read_other_parameter_value(klass, name, values_hash_from_param)
-
end
-
end
-
-
1
def read_time_parameter_value(name, values_hash_from_param)
-
# If Date bits were not provided, error
-
raise "Missing Parameter" if [1,2,3].any?{|position| !values_hash_from_param.has_key?(position)}
-
max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param, 6)
-
set_values = (1..max_position).collect{|position| values_hash_from_param[position] }
-
# If Date bits were provided but blank, then default to 1
-
# If Time bits are not there, then default to 0
-
[1,1,1,0,0,0].each_with_index{|v,i| set_values[i] = set_values[i].blank? ? v : set_values[i]}
-
instantiate_time_object(name, set_values)
-
end
-
-
1
def read_date_parameter_value(name, values_hash_from_param)
-
set_values = (1..3).collect{|position| values_hash_from_param[position].blank? ? 1 : values_hash_from_param[position]}
-
begin
-
Date.new(*set_values)
-
rescue ArgumentError => ex # if Date.new raises an exception on an invalid date
-
instantiate_time_object(name, set_values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
-
end
-
end
-
-
1
def read_other_parameter_value(klass, name, values_hash_from_param)
-
max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param)
-
values = (1..max_position).collect do |position|
-
raise "Missing Parameter" if !values_hash_from_param.has_key?(position)
-
values_hash_from_param[position]
-
end
-
klass.new(*values)
-
end
-
-
1
def extract_max_param_for_multiparameter_attributes(values_hash_from_param, upper_cap = 100)
-
[values_hash_from_param.keys.max,upper_cap].min
-
end
-
-
1
def extract_callstack_for_multiparameter_attributes(pairs)
-
3
attributes = { }
-
-
3
pairs.each do |pair|
-
multiparameter_name, value = pair
-
attribute_name = multiparameter_name.split("(").first
-
attributes[attribute_name] = {} unless attributes.include?(attribute_name)
-
-
parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
-
attributes[attribute_name][find_parameter_position(multiparameter_name)] ||= parameter_value
-
end
-
-
3
attributes
-
end
-
-
1
def type_cast_attribute_value(multiparameter_name, value)
-
multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
-
end
-
-
1
def find_parameter_position(multiparameter_name)
-
multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i
-
end
-
-
# Returns a comma-separated pair list, like "key1 = val1, key2 = val2".
-
1
def comma_pair_list(hash)
-
hash.map { |k,v| "#{k} = #{v}" }.join(", ")
-
end
-
-
1
def quote_columns(quoter, hash)
-
Hash[hash.map { |name, value| [quoter.quote_column_name(name), value] }]
-
end
-
-
1
def quoted_comma_pair_list(quoter, hash)
-
comma_pair_list(quote_columns(quoter, hash))
-
end
-
-
1
def convert_number_column_value(value)
-
if value == false
-
0
-
elsif value == true
-
1
-
elsif value.is_a?(String) && value.blank?
-
nil
-
else
-
value
-
end
-
end
-
-
1
def populate_with_current_scope_attributes
-
3
return unless self.class.scope_attributes?
-
-
self.class.scope_attributes.each do |att,value|
-
send("#{att}=", value) if respond_to?("#{att}=")
-
end
-
end
-
-
# Clear attributes and changed_attributes
-
1
def clear_timestamp_attributes
-
all_timestamp_attributes_in_model.each do |attribute_name|
-
self[attribute_name] = nil
-
changed_attributes.delete(attribute_name)
-
end
-
end
-
end
-
-
1
Base.class_eval do
-
1
include ActiveRecord::Persistence
-
1
extend ActiveModel::Naming
-
1
extend QueryCache::ClassMethods
-
1
extend ActiveSupport::Benchmarkable
-
1
extend ActiveSupport::DescendantsTracker
-
-
1
include ActiveModel::Conversion
-
1
include Validations
-
1
extend CounterCache
-
1
include Locking::Optimistic, Locking::Pessimistic
-
1
include AttributeMethods
-
1
include AttributeMethods::Read, AttributeMethods::Write, AttributeMethods::BeforeTypeCast, AttributeMethods::Query
-
1
include AttributeMethods::PrimaryKey
-
1
include AttributeMethods::TimeZoneConversion
-
1
include AttributeMethods::Dirty
-
1
include ActiveModel::MassAssignmentSecurity
-
1
include Callbacks, ActiveModel::Observing, Timestamp
-
1
include Associations, NamedScope
-
1
include IdentityMap
-
1
include ActiveModel::SecurePassword
-
-
# AutosaveAssociation needs to be included before Transactions, because we want
-
# #save_with_autosave_associations to be wrapped inside a transaction.
-
1
include AutosaveAssociation, NestedAttributes
-
1
include Aggregations, Transactions, Reflection, Serialization
-
-
1
NilClass.add_whiner(self) if NilClass.respond_to?(:add_whiner)
-
-
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
-
# "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
-
# (Alias for the protected read_attribute method).
-
1
alias [] read_attribute
-
-
# Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
-
# (Alias for the protected write_attribute method).
-
1
alias []= write_attribute
-
-
1
public :[], :[]=
-
end
-
end
-
-
# TODO: Remove this and make it work with LAZY flag
-
1
require 'active_record/connection_adapters/abstract_adapter'
-
1
ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
-
1
require 'active_support/core_ext/array/wrap'
-
-
1
module ActiveRecord
-
# = Active Record Callbacks
-
#
-
# Callbacks are hooks into the life cycle of an Active Record object that allow you to trigger logic
-
# before or after an alteration of the object state. This can be used to make sure that associated and
-
# dependent objects are deleted when +destroy+ is called (by overwriting +before_destroy+) or to massage attributes
-
# before they're validated (by overwriting +before_validation+). As an example of the callbacks initiated, consider
-
# the <tt>Base#save</tt> call for a new record:
-
#
-
# * (-) <tt>save</tt>
-
# * (-) <tt>valid</tt>
-
# * (1) <tt>before_validation</tt>
-
# * (-) <tt>validate</tt>
-
# * (2) <tt>after_validation</tt>
-
# * (3) <tt>before_save</tt>
-
# * (4) <tt>before_create</tt>
-
# * (-) <tt>create</tt>
-
# * (5) <tt>after_create</tt>
-
# * (6) <tt>after_save</tt>
-
# * (7) <tt>after_commit</tt>
-
#
-
# Also, an <tt>after_rollback</tt> callback can be configured to be triggered whenever a rollback is issued.
-
# Check out <tt>ActiveRecord::Transactions</tt> for more details about <tt>after_commit</tt> and
-
# <tt>after_rollback</tt>.
-
#
-
# Lastly an <tt>after_find</tt> and <tt>after_initialize</tt> callback is triggered for each object that
-
# is found and instantiated by a finder, with <tt>after_initialize</tt> being triggered after new objects
-
# are instantiated as well.
-
#
-
# That's a total of twelve callbacks, which gives you immense power to react and prepare for each state in the
-
# Active Record life cycle. The sequence for calling <tt>Base#save</tt> for an existing record is similar,
-
# except that each <tt>_create</tt> callback is replaced by the corresponding <tt>_update</tt> callback.
-
#
-
# Examples:
-
# class CreditCard < ActiveRecord::Base
-
# # Strip everything but digits, so the user can specify "555 234 34" or
-
# # "5552-3434" or both will mean "55523434"
-
# before_validation(:on => :create) do
-
# self.number = number.gsub(/[^0-9]/, "") if attribute_present?("number")
-
# end
-
# end
-
#
-
# class Subscription < ActiveRecord::Base
-
# before_create :record_signup
-
#
-
# private
-
# def record_signup
-
# self.signed_up_on = Date.today
-
# end
-
# end
-
#
-
# class Firm < ActiveRecord::Base
-
# # Destroys the associated clients and people when the firm is destroyed
-
# before_destroy { |record| Person.destroy_all "firm_id = #{record.id}" }
-
# before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
-
# end
-
#
-
# == Inheritable callback queues
-
#
-
# Besides the overwritable callback methods, it's also possible to register callbacks through the
-
# use of the callback macros. Their main advantage is that the macros add behavior into a callback
-
# queue that is kept intact down through an inheritance hierarchy.
-
#
-
# class Topic < ActiveRecord::Base
-
# before_destroy :destroy_author
-
# end
-
#
-
# class Reply < Topic
-
# before_destroy :destroy_readers
-
# end
-
#
-
# Now, when <tt>Topic#destroy</tt> is run only +destroy_author+ is called. When <tt>Reply#destroy</tt> is
-
# run, both +destroy_author+ and +destroy_readers+ are called. Contrast this to the following situation
-
# where the +before_destroy+ method is overridden:
-
#
-
# class Topic < ActiveRecord::Base
-
# def before_destroy() destroy_author end
-
# end
-
#
-
# class Reply < Topic
-
# def before_destroy() destroy_readers end
-
# end
-
#
-
# In that case, <tt>Reply#destroy</tt> would only run +destroy_readers+ and _not_ +destroy_author+.
-
# So, use the callback macros when you want to ensure that a certain callback is called for the entire
-
# hierarchy, and use the regular overwriteable methods when you want to leave it up to each descendant
-
# to decide whether they want to call +super+ and trigger the inherited callbacks.
-
#
-
# *IMPORTANT:* In order for inheritance to work for the callback queues, you must specify the
-
# callbacks before specifying the associations. Otherwise, you might trigger the loading of a
-
# child before the parent has registered the callbacks and they won't be inherited.
-
#
-
# == Types of callbacks
-
#
-
# There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
-
# inline methods (using a proc), and inline eval methods (using a string). Method references and callback objects
-
# are the recommended approaches, inline methods using a proc are sometimes appropriate (such as for
-
# creating mix-ins), and inline eval methods are deprecated.
-
#
-
# The method reference callbacks work by specifying a protected or private method available in the object, like this:
-
#
-
# class Topic < ActiveRecord::Base
-
# before_destroy :delete_parents
-
#
-
# private
-
# def delete_parents
-
# self.class.delete_all "parent_id = #{id}"
-
# end
-
# end
-
#
-
# The callback objects have methods named after the callback called with the record as the only parameter, such as:
-
#
-
# class BankAccount < ActiveRecord::Base
-
# before_save EncryptionWrapper.new
-
# after_save EncryptionWrapper.new
-
# after_initialize EncryptionWrapper.new
-
# end
-
#
-
# class EncryptionWrapper
-
# def before_save(record)
-
# record.credit_card_number = encrypt(record.credit_card_number)
-
# end
-
#
-
# def after_save(record)
-
# record.credit_card_number = decrypt(record.credit_card_number)
-
# end
-
#
-
# alias_method :after_find, :after_save
-
#
-
# private
-
# def encrypt(value)
-
# # Secrecy is committed
-
# end
-
#
-
# def decrypt(value)
-
# # Secrecy is unveiled
-
# end
-
# end
-
#
-
# So you specify the object you want messaged on a given callback. When that callback is triggered, the object has
-
# a method by the name of the callback messaged. You can make these callbacks more flexible by passing in other
-
# initialization data such as the name of the attribute to work with:
-
#
-
# class BankAccount < ActiveRecord::Base
-
# before_save EncryptionWrapper.new("credit_card_number")
-
# after_save EncryptionWrapper.new("credit_card_number")
-
# after_initialize EncryptionWrapper.new("credit_card_number")
-
# end
-
#
-
# class EncryptionWrapper
-
# def initialize(attribute)
-
# @attribute = attribute
-
# end
-
#
-
# def before_save(record)
-
# record.send("#{@attribute}=", encrypt(record.send("#{@attribute}")))
-
# end
-
#
-
# def after_save(record)
-
# record.send("#{@attribute}=", decrypt(record.send("#{@attribute}")))
-
# end
-
#
-
# alias_method :after_find, :after_save
-
#
-
# private
-
# def encrypt(value)
-
# # Secrecy is committed
-
# end
-
#
-
# def decrypt(value)
-
# # Secrecy is unveiled
-
# end
-
# end
-
#
-
# The callback macros usually accept a symbol for the method they're supposed to run, but you can also
-
# pass a "method string", which will then be evaluated within the binding of the callback. Example:
-
#
-
# class Topic < ActiveRecord::Base
-
# before_destroy 'self.class.delete_all "parent_id = #{id}"'
-
# end
-
#
-
# Notice that single quotes (') are used so the <tt>#{id}</tt> part isn't evaluated until the callback
-
# is triggered. Also note that these inline callbacks can be stacked just like the regular ones:
-
#
-
# class Topic < ActiveRecord::Base
-
# before_destroy 'self.class.delete_all "parent_id = #{id}"',
-
# 'puts "Evaluated after parents are destroyed"'
-
# end
-
#
-
# == <tt>before_validation*</tt> returning statements
-
#
-
# If the returning value of a +before_validation+ callback can be evaluated to +false+, the process will be
-
# aborted and <tt>Base#save</tt> will return +false+. If Base#save! is called it will raise a
-
# ActiveRecord::RecordInvalid exception. Nothing will be appended to the errors object.
-
#
-
# == Canceling callbacks
-
#
-
# If a <tt>before_*</tt> callback returns +false+, all the later callbacks and the associated action are
-
# cancelled. If an <tt>after_*</tt> callback returns +false+, all the later callbacks are cancelled.
-
# Callbacks are generally run in the order they are defined, with the exception of callbacks defined as
-
# methods on the model, which are called last.
-
#
-
# == Transactions
-
#
-
# The entire callback chain of a +save+, <tt>save!</tt>, or +destroy+ call runs
-
# within a transaction. That includes <tt>after_*</tt> hooks. If everything
-
# goes fine a COMMIT is executed once the chain has been completed.
-
#
-
# If a <tt>before_*</tt> callback cancels the action a ROLLBACK is issued. You
-
# can also trigger a ROLLBACK raising an exception in any of the callbacks,
-
# including <tt>after_*</tt> hooks. Note, however, that in that case the client
-
# needs to be aware of it because an ordinary +save+ will raise such exception
-
# instead of quietly returning +false+.
-
#
-
# == Debugging callbacks
-
#
-
# The callback chain is accessible via the <tt>_*_callbacks</tt> method on an object. ActiveModel Callbacks support
-
# <tt>:before</tt>, <tt>:after</tt> and <tt>:around</tt> as values for the <tt>kind</tt> property. The <tt>kind</tt> property
-
# defines what part of the chain the callback runs in.
-
#
-
# To find all callbacks in the before_save callback chain:
-
#
-
# Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }
-
#
-
# Returns an array of callback objects that form the before_save chain.
-
#
-
# To further check if the before_save chain contains a proc defined as <tt>rest_when_dead</tt> use the <tt>filter</tt> property of the callback object:
-
#
-
# Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }.collect(&:filter).include?(:rest_when_dead)
-
#
-
# Returns true or false depending on whether the proc is contained in the before_save callback chain on a Topic model.
-
#
-
1
module Callbacks
-
1
extend ActiveSupport::Concern
-
-
1
CALLBACKS = [
-
:after_initialize, :after_find, :after_touch, :before_validation, :after_validation,
-
:before_save, :around_save, :after_save, :before_create, :around_create,
-
:after_create, :before_update, :around_update, :after_update,
-
:before_destroy, :around_destroy, :after_destroy, :after_commit, :after_rollback
-
]
-
-
1
included do
-
1
extend ActiveModel::Callbacks
-
1
include ActiveModel::Validations::Callbacks
-
-
1
define_model_callbacks :initialize, :find, :touch, :only => :after
-
1
define_model_callbacks :save, :create, :update, :destroy
-
end
-
-
1
def destroy #:nodoc:
-
run_callbacks(:destroy) { super }
-
end
-
-
1
def touch(*) #:nodoc:
-
run_callbacks(:touch) { super }
-
end
-
-
1
private
-
-
1
def create_or_update #:nodoc:
-
run_callbacks(:save) { super }
-
end
-
-
1
def create #:nodoc:
-
run_callbacks(:create) { super }
-
end
-
-
1
def update(*) #:nodoc:
-
run_callbacks(:update) { super }
-
end
-
end
-
end
-
1
require 'thread'
-
1
require 'monitor'
-
1
require 'set'
-
1
require 'active_support/core_ext/module/synchronization'
-
-
1
module ActiveRecord
-
# Raised when a connection could not be obtained within the connection
-
# acquisition timeout period.
-
1
class ConnectionTimeoutError < ConnectionNotEstablished
-
end
-
-
1
module ConnectionAdapters
-
# Connection pool base class for managing Active Record database
-
# connections.
-
#
-
# == Introduction
-
#
-
# A connection pool synchronizes thread access to a limited number of
-
# database connections. The basic idea is that each thread checks out a
-
# database connection from the pool, uses that connection, and checks the
-
# connection back in. ConnectionPool is completely thread-safe, and will
-
# ensure that a connection cannot be used by two threads at the same time,
-
# as long as ConnectionPool's contract is correctly followed. It will also
-
# handle cases in which there are more threads than connections: if all
-
# connections have been checked out, and a thread tries to checkout a
-
# connection anyway, then ConnectionPool will wait until some other thread
-
# has checked in a connection.
-
#
-
# == Obtaining (checking out) a connection
-
#
-
# Connections can be obtained and used from a connection pool in several
-
# ways:
-
#
-
# 1. Simply use ActiveRecord::Base.connection as with Active Record 2.1 and
-
# earlier (pre-connection-pooling). Eventually, when you're done with
-
# the connection(s) and wish it to be returned to the pool, you call
-
# ActiveRecord::Base.clear_active_connections!. This will be the
-
# default behavior for Active Record when used in conjunction with
-
# Action Pack's request handling cycle.
-
# 2. Manually check out a connection from the pool with
-
# ActiveRecord::Base.connection_pool.checkout. You are responsible for
-
# returning this connection to the pool when finished by calling
-
# ActiveRecord::Base.connection_pool.checkin(connection).
-
# 3. Use ActiveRecord::Base.connection_pool.with_connection(&block), which
-
# obtains a connection, yields it as the sole argument to the block,
-
# and returns it to the pool after the block completes.
-
#
-
# Connections in the pool are actually AbstractAdapter objects (or objects
-
# compatible with AbstractAdapter's interface).
-
#
-
# == Options
-
#
-
# There are two connection-pooling-related options that you can add to
-
# your database connection configuration:
-
#
-
# * +pool+: number indicating size of connection pool (default 5)
-
# * +wait_timeout+: number of seconds to block and wait for a connection
-
# before giving up and raising a timeout error (default 5 seconds).
-
1
class ConnectionPool
-
1
attr_accessor :automatic_reconnect
-
1
attr_reader :spec, :connections
-
1
attr_reader :columns, :columns_hash, :primary_keys, :tables
-
1
attr_reader :column_defaults
-
-
# Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
-
# object which describes database connection information (e.g. adapter,
-
# host name, username, password, etc), as well as the maximum size for
-
# this ConnectionPool.
-
#
-
# The default ConnectionPool maximum size is 5.
-
1
def initialize(spec)
-
1
@spec = spec
-
-
# The cache of reserved connections mapped to threads
-
1
@reserved_connections = {}
-
-
# The mutex used to synchronize pool access
-
1
@connection_mutex = Monitor.new
-
1
@queue = @connection_mutex.new_cond
-
1
@timeout = spec.config[:wait_timeout] || 5
-
-
# default max pool size to 5
-
1
@size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
-
-
1
@connections = []
-
1
@checked_out = []
-
1
@automatic_reconnect = true
-
1
@tables = {}
-
1
@visitor = nil
-
-
1
@columns = Hash.new do |h, table_name|
-
2
h[table_name] = with_connection do |conn|
-
-
# Fetch a list of columns
-
2
conn.columns(table_name, "#{table_name} Columns").tap do |columns|
-
-
# set primary key information
-
2
columns.each do |column|
-
14
column.primary = column.name == primary_keys[table_name]
-
end
-
end
-
end
-
end
-
-
1
@columns_hash = Hash.new do |h, table_name|
-
1
h[table_name] = Hash[columns[table_name].map { |col|
-
7
[col.name, col]
-
}]
-
end
-
-
1
@column_defaults = Hash.new do |h, table_name|
-
1
h[table_name] = Hash[columns[table_name].map { |col|
-
7
[col.name, col.default]
-
}]
-
end
-
-
1
@primary_keys = Hash.new do |h, table_name|
-
2
h[table_name] = with_connection do |conn|
-
2
table_exists?(table_name) ? conn.primary_key(table_name) : 'id'
-
end
-
end
-
end
-
-
# A cached lookup for table existence.
-
1
def table_exists?(name)
-
2
return true if @tables.key? name
-
-
1
with_connection do |conn|
-
5
conn.tables.each { |table| @tables[table] = true }
-
1
@tables[name] = true if !@tables.key?(name) && conn.table_exists?(name)
-
end
-
-
1
@tables.key? name
-
end
-
-
# Clears out internal caches:
-
#
-
# * columns
-
# * columns_hash
-
# * tables
-
1
def clear_cache!
-
@columns.clear
-
@columns_hash.clear
-
@column_defaults.clear
-
@tables.clear
-
end
-
-
# Clear out internal caches for table with +table_name+.
-
1
def clear_table_cache!(table_name)
-
@columns.delete table_name
-
@columns_hash.delete table_name
-
@column_defaults.delete table_name
-
@primary_keys.delete table_name
-
end
-
-
# Retrieve the connection associated with the current thread, or call
-
# #checkout to obtain one if necessary.
-
#
-
# #connection can be called any number of times; the connection is
-
# held in a hash keyed by the thread id.
-
1
def connection
-
9
@reserved_connections[current_connection_id] ||= checkout
-
end
-
-
# Check to see if there is an active connection in this connection
-
# pool.
-
1
def active_connection?
-
@reserved_connections.key? current_connection_id
-
end
-
-
# Signal that the thread is finished with the current connection.
-
# #release_connection releases the connection-thread association
-
# and returns the connection to the pool.
-
1
def release_connection(with_id = current_connection_id)
-
conn = @reserved_connections.delete(with_id)
-
checkin conn if conn
-
end
-
-
# If a connection already exists yield it to the block. If no connection
-
# exists checkout a connection, yield it to the block, and checkin the
-
# connection when finished.
-
1
def with_connection
-
5
connection_id = current_connection_id
-
5
fresh_connection = true unless @reserved_connections[connection_id]
-
5
yield connection
-
ensure
-
5
release_connection(connection_id) if fresh_connection
-
end
-
-
# Returns true if a connection has already been opened.
-
1
def connected?
-
!@connections.empty?
-
end
-
-
# Disconnects all connections in the pool, and clears the pool.
-
1
def disconnect!
-
@reserved_connections.each do |name,conn|
-
checkin conn
-
end
-
@reserved_connections = {}
-
@connections.each do |conn|
-
conn.disconnect!
-
end
-
@connections = []
-
end
-
-
# Clears the cache which maps classes.
-
1
def clear_reloadable_connections!
-
@reserved_connections.each do |name, conn|
-
checkin conn
-
end
-
@reserved_connections = {}
-
@connections.each do |conn|
-
conn.disconnect! if conn.requires_reloading?
-
end
-
@connections.delete_if do |conn|
-
conn.requires_reloading?
-
end
-
end
-
-
# Verify active connections and remove and disconnect connections
-
# associated with stale threads.
-
1
def verify_active_connections! #:nodoc:
-
clear_stale_cached_connections!
-
@connections.each do |connection|
-
connection.verify!
-
end
-
end
-
-
# Return any checked-out connections back to the pool by threads that
-
# are no longer alive.
-
1
def clear_stale_cached_connections!
-
keys = @reserved_connections.keys - Thread.list.find_all { |t|
-
t.alive?
-
}.map { |thread| thread.object_id }
-
keys.each do |key|
-
checkin @reserved_connections[key]
-
@reserved_connections.delete(key)
-
end
-
end
-
-
# Check-out a database connection from the pool, indicating that you want
-
# to use it. You should call #checkin when you no longer need this.
-
#
-
# This is done by either returning an existing connection, or by creating
-
# a new connection. If the maximum number of connections for this pool has
-
# already been reached, but the pool is empty (i.e. they're all being used),
-
# then this method will wait until a thread has checked in a connection.
-
# The wait time is bounded however: if no connection can be checked out
-
# within the timeout specified for this pool, then a ConnectionTimeoutError
-
# exception will be raised.
-
#
-
# Returns: an AbstractAdapter object.
-
#
-
# Raises:
-
# - ConnectionTimeoutError: no connection can be obtained from the pool
-
# within the timeout period.
-
1
def checkout
-
# Checkout an available connection
-
1
@connection_mutex.synchronize do
-
1
loop do
-
1
conn = if @checked_out.size < @connections.size
-
checkout_existing_connection
-
elsif @connections.size < @size
-
1
checkout_new_connection
-
end
-
1
return conn if conn
-
-
@queue.wait(@timeout)
-
-
if(@checked_out.size < @connections.size)
-
next
-
else
-
clear_stale_cached_connections!
-
if @size == @checked_out.size
-
raise ConnectionTimeoutError, "could not obtain a database connection#{" within #{@timeout} seconds" if @timeout}. The max pool size is currently #{@size}; consider increasing it."
-
end
-
end
-
-
end
-
end
-
end
-
-
# Check-in a database connection back into the pool, indicating that you
-
# no longer need this connection.
-
#
-
# +conn+: an AbstractAdapter object, which was obtained by earlier by
-
# calling +checkout+ on this pool.
-
1
def checkin(conn)
-
@connection_mutex.synchronize do
-
conn.run_callbacks :checkin do
-
@checked_out.delete conn
-
@queue.signal
-
end
-
end
-
end
-
-
1
synchronize :clear_reloadable_connections!, :verify_active_connections!,
-
:connected?, :disconnect!, :with => :@connection_mutex
-
-
1
private
-
-
1
def new_connection
-
1
connection = ActiveRecord::Base.send(spec.adapter_method, spec.config)
-
-
# TODO: This is a bit icky, and in the long term we may want to change the method
-
# signature for connections. Also, if we switch to have one visitor per
-
# connection (and therefore per thread), we can get rid of the thread-local
-
# variable in Arel::Visitors::ToSql.
-
1
@visitor ||= connection.class.visitor_for(self)
-
1
connection.visitor = @visitor
-
-
1
connection
-
end
-
-
1
def current_connection_id #:nodoc:
-
14
Thread.current.object_id
-
end
-
-
1
def checkout_new_connection
-
1
raise ConnectionNotEstablished unless @automatic_reconnect
-
-
1
c = new_connection
-
1
@connections << c
-
1
checkout_and_verify(c)
-
end
-
-
1
def checkout_existing_connection
-
c = (@connections - @checked_out).first
-
checkout_and_verify(c)
-
end
-
-
1
def checkout_and_verify(c)
-
1
c.run_callbacks :checkout do
-
1
c.verify!
-
1
@checked_out << c
-
end
-
1
c
-
end
-
end
-
-
# ConnectionHandler is a collection of ConnectionPool objects. It is used
-
# for keeping separate connection pools for Active Record models that connect
-
# to different databases.
-
#
-
# For example, suppose that you have 5 models, with the following hierarchy:
-
#
-
# |
-
# +-- Book
-
# | |
-
# | +-- ScaryBook
-
# | +-- GoodBook
-
# +-- Author
-
# +-- BankAccount
-
#
-
# Suppose that Book is to connect to a separate database (i.e. one other
-
# than the default database). Then Book, ScaryBook and GoodBook will all use
-
# the same connection pool. Likewise, Author and BankAccount will use the
-
# same connection pool. However, the connection pool used by Author/BankAccount
-
# is not the same as the one used by Book/ScaryBook/GoodBook.
-
#
-
# Normally there is only a single ConnectionHandler instance, accessible via
-
# ActiveRecord::Base.connection_handler. Active Record models use this to
-
# determine that connection pool that they should use.
-
1
class ConnectionHandler
-
1
attr_reader :connection_pools
-
-
1
def initialize(pools = {})
-
1
@connection_pools = pools
-
end
-
-
1
def establish_connection(name, spec)
-
1
@connection_pools[name] = ConnectionAdapters::ConnectionPool.new(spec)
-
end
-
-
# Returns true if there are any active connections among the connection
-
# pools that the ConnectionHandler is managing.
-
1
def active_connections?
-
connection_pools.values.any? { |pool| pool.active_connection? }
-
end
-
-
# Returns any connections in use by the current thread back to the pool,
-
# and also returns connections to the pool cached by threads that are no
-
# longer alive.
-
1
def clear_active_connections!
-
@connection_pools.each_value {|pool| pool.release_connection }
-
end
-
-
# Clears the cache which maps classes.
-
1
def clear_reloadable_connections!
-
@connection_pools.each_value {|pool| pool.clear_reloadable_connections! }
-
end
-
-
1
def clear_all_connections!
-
@connection_pools.each_value {|pool| pool.disconnect! }
-
end
-
-
# Verify active connections.
-
1
def verify_active_connections! #:nodoc:
-
@connection_pools.each_value {|pool| pool.verify_active_connections! }
-
end
-
-
# Locate the connection of the nearest super class. This can be an
-
# active or defined connection: if it is the latter, it will be
-
# opened and set as the active connection for the class it was defined
-
# for (not necessarily the current class).
-
1
def retrieve_connection(klass) #:nodoc:
-
4
pool = retrieve_connection_pool(klass)
-
4
(pool && pool.connection) or raise ConnectionNotEstablished
-
end
-
-
# Returns true if a connection that's accessible to this class has
-
# already been opened.
-
1
def connected?(klass)
-
conn = retrieve_connection_pool(klass)
-
conn && conn.connected?
-
end
-
-
# Remove the connection for this class. This will close the active
-
# connection and the defined connection (if they exist). The result
-
# can be used as an argument for establish_connection, for easily
-
# re-establishing the connection.
-
1
def remove_connection(klass)
-
1
pool = @connection_pools[klass.name]
-
1
return nil unless pool
-
-
pool.automatic_reconnect = false
-
pool.disconnect!
-
pool.spec.config
-
end
-
-
1
def retrieve_connection_pool(klass)
-
96
pool = @connection_pools[klass.name]
-
96
return pool if pool
-
48
return nil if ActiveRecord::Base == klass
-
48
retrieve_connection_pool klass.superclass
-
end
-
end
-
-
1
class ConnectionManagement
-
1
class Proxy # :nodoc:
-
1
attr_reader :body, :testing
-
-
1
def initialize(body, testing = false)
-
@body = body
-
@testing = testing
-
end
-
-
1
def method_missing(method_sym, *arguments, &block)
-
@body.send(method_sym, *arguments, &block)
-
end
-
-
1
def respond_to?(method_sym, include_private = false)
-
super || @body.respond_to?(method_sym)
-
end
-
-
1
def each(&block)
-
body.each(&block)
-
end
-
-
1
def close
-
body.close if body.respond_to?(:close)
-
-
# Don't return connection (and perform implicit rollback) if
-
# this request is a part of integration test
-
ActiveRecord::Base.clear_active_connections! unless testing
-
end
-
end
-
-
1
def initialize(app)
-
1
@app = app
-
end
-
-
1
def call(env)
-
testing = env.key?('rack.test')
-
-
status, headers, body = @app.call(env)
-
-
[status, headers, Proxy.new(body, testing)]
-
rescue
-
ActiveRecord::Base.clear_active_connections! unless testing
-
raise
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
class Base
-
1
class ConnectionSpecification #:nodoc:
-
1
attr_reader :config, :adapter_method
-
1
def initialize (config, adapter_method)
-
1
@config, @adapter_method = config, adapter_method
-
end
-
end
-
-
##
-
# :singleton-method:
-
# The connection handler
-
1
class_attribute :connection_handler, :instance_writer => false
-
1
self.connection_handler = ConnectionAdapters::ConnectionHandler.new
-
-
# Returns the connection currently associated with the class. This can
-
# also be used to "borrow" the connection to do database work that isn't
-
# easily done without going straight to SQL.
-
1
def connection
-
self.class.connection
-
end
-
-
# Establishes the connection to the database. Accepts a hash as input where
-
# the <tt>:adapter</tt> key must be specified with the name of a database adapter (in lower-case)
-
# example for regular databases (MySQL, Postgresql, etc):
-
#
-
# ActiveRecord::Base.establish_connection(
-
# :adapter => "mysql",
-
# :host => "localhost",
-
# :username => "myuser",
-
# :password => "mypass",
-
# :database => "somedatabase"
-
# )
-
#
-
# Example for SQLite database:
-
#
-
# ActiveRecord::Base.establish_connection(
-
# :adapter => "sqlite",
-
# :database => "path/to/dbfile"
-
# )
-
#
-
# Also accepts keys as strings (for parsing from YAML for example):
-
#
-
# ActiveRecord::Base.establish_connection(
-
# "adapter" => "sqlite",
-
# "database" => "path/to/dbfile"
-
# )
-
#
-
# The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
-
# may be returned on an error.
-
1
def self.establish_connection(spec = nil)
-
4
case spec
-
when nil
-
1
raise AdapterNotSpecified unless defined?(Rails.env)
-
1
establish_connection(Rails.env)
-
when ConnectionSpecification
-
1
self.connection_handler.establish_connection(name, spec)
-
when Symbol, String
-
1
if configuration = configurations[spec.to_s]
-
1
establish_connection(configuration)
-
else
-
raise AdapterNotSpecified, "#{spec} database is not configured"
-
end
-
else
-
1
spec = spec.symbolize_keys
-
1
unless spec.key?(:adapter) then raise AdapterNotSpecified, "database configuration does not specify adapter" end
-
-
1
begin
-
1
require "active_record/connection_adapters/#{spec[:adapter]}_adapter"
-
rescue LoadError => e
-
raise "Please install the #{spec[:adapter]} adapter: `gem install activerecord-#{spec[:adapter]}-adapter` (#{e})"
-
end
-
-
1
adapter_method = "#{spec[:adapter]}_connection"
-
1
unless respond_to?(adapter_method)
-
raise AdapterNotFound, "database configuration specifies nonexistent #{spec[:adapter]} adapter"
-
end
-
-
1
remove_connection
-
1
establish_connection(ConnectionSpecification.new(spec, adapter_method))
-
end
-
end
-
-
1
class << self
-
# Returns the connection currently associated with the class. This can
-
# also be used to "borrow" the connection to do database work unrelated
-
# to any of the specific Active Records.
-
1
def connection
-
4
retrieve_connection
-
end
-
-
# Returns the configuration of the associated connection as a hash:
-
#
-
# ActiveRecord::Base.connection_config
-
# # => {:pool=>5, :timeout=>5000, :database=>"db/development.sqlite3", :adapter=>"sqlite3"}
-
#
-
# Please use only for reading.
-
1
def connection_config
-
connection_pool.spec.config
-
end
-
-
1
def connection_pool
-
44
connection_handler.retrieve_connection_pool(self)
-
end
-
-
1
def retrieve_connection
-
4
connection_handler.retrieve_connection(self)
-
end
-
-
# Returns true if Active Record is connected.
-
1
def connected?
-
connection_handler.connected?(self)
-
end
-
-
1
def remove_connection(klass = self)
-
1
connection_handler.remove_connection(klass)
-
end
-
-
1
def clear_active_connections!
-
connection_handler.clear_active_connections!
-
end
-
-
1
delegate :clear_reloadable_connections!,
-
:clear_all_connections!,:verify_active_connections!, :to => :connection_handler
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module ConnectionAdapters # :nodoc:
-
1
module DatabaseLimits
-
-
# Returns the maximum length of a table alias.
-
1
def table_alias_length
-
255
-
end
-
-
# Returns the maximum length of a column name.
-
1
def column_name_length
-
64
-
end
-
-
# Returns the maximum length of a table name.
-
1
def table_name_length
-
64
-
end
-
-
# Returns the maximum length of an index name.
-
1
def index_name_length
-
64
-
end
-
-
# Returns the maximum number of columns per table.
-
1
def columns_per_table
-
1024
-
end
-
-
# Returns the maximum number of indexes per table.
-
1
def indexes_per_table
-
16
-
end
-
-
# Returns the maximum number of columns in a multicolumn index.
-
1
def columns_per_multicolumn_index
-
16
-
end
-
-
# Returns the maximum number of elements in an IN (x,y,z) clause.
-
# nil means no limit.
-
1
def in_clause_length
-
nil
-
end
-
-
# Returns the maximum length of an SQL query.
-
1
def sql_query_length
-
1048575
-
end
-
-
# Returns maximum number of joins in a single query.
-
1
def joins_per_query
-
256
-
end
-
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/deprecation'
-
-
1
module ActiveRecord
-
1
module ConnectionAdapters # :nodoc:
-
1
module DatabaseStatements
-
# Converts an arel AST to SQL
-
1
def to_sql(arel)
-
if arel.respond_to?(:ast)
-
visitor.accept(arel.ast)
-
else
-
arel
-
end
-
end
-
-
# Returns an array of record hashes with the column names as keys and
-
# column values as values.
-
1
def select_all(arel, name = nil, binds = [])
-
select(to_sql(arel), name, binds)
-
end
-
-
# Returns a record hash with the column names as keys and column values
-
# as values.
-
1
def select_one(arel, name = nil)
-
result = select_all(arel, name)
-
result.first if result
-
end
-
-
# Returns a single value from a record
-
1
def select_value(arel, name = nil)
-
if result = select_one(arel, name)
-
result.values.first
-
end
-
end
-
-
# Returns an array of the values of the first column in a select:
-
# select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
-
1
def select_values(arel, name = nil)
-
result = select_rows(to_sql(arel), name)
-
result.map { |v| v[0] }
-
end
-
-
# Returns an array of arrays containing the field values.
-
# Order is the same as that returned by +columns+.
-
1
def select_rows(sql, name = nil)
-
end
-
1
undef_method :select_rows
-
-
# Executes the SQL statement in the context of this connection.
-
1
def execute(sql, name = nil)
-
end
-
1
undef_method :execute
-
-
# Executes +sql+ statement in the context of this connection using
-
# +binds+ as the bind substitutes. +name+ is logged along with
-
# the executed +sql+ statement.
-
1
def exec_query(sql, name = 'SQL', binds = [])
-
end
-
-
# Executes insert +sql+ statement in the context of this connection using
-
# +binds+ as the bind substitutes. +name+ is the logged along with
-
# the executed +sql+ statement.
-
1
def exec_insert(sql, name, binds)
-
exec_query(sql, name, binds)
-
end
-
-
# Executes delete +sql+ statement in the context of this connection using
-
# +binds+ as the bind substitutes. +name+ is the logged along with
-
# the executed +sql+ statement.
-
1
def exec_delete(sql, name, binds)
-
exec_query(sql, name, binds)
-
end
-
-
# Executes update +sql+ statement in the context of this connection using
-
# +binds+ as the bind substitutes. +name+ is the logged along with
-
# the executed +sql+ statement.
-
1
def exec_update(sql, name, binds)
-
exec_query(sql, name, binds)
-
end
-
-
# Returns the last auto-generated ID from the affected table.
-
#
-
# +id_value+ will be returned unless the value is nil, in
-
# which case the database will attempt to calculate the last inserted
-
# id and return that value.
-
#
-
# If the next id was calculated in advance (as in Oracle), it should be
-
# passed in as +id_value+.
-
1
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
-
sql, binds = sql_for_insert(to_sql(arel), pk, id_value, sequence_name, binds)
-
value = exec_insert(sql, name, binds)
-
id_value || last_inserted_id(value)
-
end
-
-
# Executes the update statement and returns the number of rows affected.
-
1
def update(arel, name = nil, binds = [])
-
exec_update(to_sql(arel), name, binds)
-
end
-
-
# Executes the delete statement and returns the number of rows affected.
-
1
def delete(arel, name = nil, binds = [])
-
exec_delete(to_sql(arel), name, binds)
-
end
-
-
# Checks whether there is currently no transaction active. This is done
-
# by querying the database driver, and does not use the transaction
-
# house-keeping information recorded by #increment_open_transactions and
-
# friends.
-
#
-
# Returns true if there is no transaction active, false if there is a
-
# transaction active, and nil if this information is unknown.
-
#
-
# Not all adapters supports transaction state introspection. Currently,
-
# only the PostgreSQL adapter supports this.
-
1
def outside_transaction?
-
nil
-
end
-
-
# Returns +true+ when the connection adapter supports prepared statement
-
# caching, otherwise returns +false+
-
1
def supports_statement_cache?
-
false
-
end
-
-
# Runs the given block in a database transaction, and returns the result
-
# of the block.
-
#
-
# == Nested transactions support
-
#
-
# Most databases don't support true nested transactions. At the time of
-
# writing, the only database that supports true nested transactions that
-
# we're aware of, is MS-SQL.
-
#
-
# In order to get around this problem, #transaction will emulate the effect
-
# of nested transactions, by using savepoints:
-
# http://dev.mysql.com/doc/refman/5.0/en/savepoints.html
-
# Savepoints are supported by MySQL and PostgreSQL, but not SQLite3.
-
#
-
# It is safe to call this method if a database transaction is already open,
-
# i.e. if #transaction is called within another #transaction block. In case
-
# of a nested call, #transaction will behave as follows:
-
#
-
# - The block will be run without doing anything. All database statements
-
# that happen within the block are effectively appended to the already
-
# open database transaction.
-
# - However, if +:requires_new+ is set, the block will be wrapped in a
-
# database savepoint acting as a sub-transaction.
-
#
-
# === Caveats
-
#
-
# MySQL doesn't support DDL transactions. If you perform a DDL operation,
-
# then any created savepoints will be automatically released. For example,
-
# if you've created a savepoint, then you execute a CREATE TABLE statement,
-
# then the savepoint that was created will be automatically released.
-
#
-
# This means that, on MySQL, you shouldn't execute DDL operations inside
-
# a #transaction call that you know might create a savepoint. Otherwise,
-
# #transaction will raise exceptions when it tries to release the
-
# already-automatically-released savepoints:
-
#
-
# Model.connection.transaction do # BEGIN
-
# Model.connection.transaction(:requires_new => true) do # CREATE SAVEPOINT active_record_1
-
# Model.connection.create_table(...)
-
# # active_record_1 now automatically released
-
# end # RELEASE SAVEPOINT active_record_1 <--- BOOM! database error!
-
# end
-
1
def transaction(options = {})
-
options.assert_valid_keys :requires_new, :joinable
-
-
last_transaction_joinable = defined?(@transaction_joinable) ? @transaction_joinable : nil
-
if options.has_key?(:joinable)
-
@transaction_joinable = options[:joinable]
-
else
-
@transaction_joinable = true
-
end
-
requires_new = options[:requires_new] || !last_transaction_joinable
-
-
transaction_open = false
-
@_current_transaction_records ||= []
-
-
begin
-
if block_given?
-
if requires_new || open_transactions == 0
-
if open_transactions == 0
-
begin_db_transaction
-
elsif requires_new
-
create_savepoint
-
end
-
increment_open_transactions
-
transaction_open = true
-
@_current_transaction_records.push([])
-
end
-
yield
-
end
-
rescue Exception => database_transaction_rollback
-
if transaction_open && !outside_transaction?
-
transaction_open = false
-
decrement_open_transactions
-
if open_transactions == 0
-
rollback_db_transaction
-
rollback_transaction_records(true)
-
else
-
rollback_to_savepoint
-
rollback_transaction_records(false)
-
end
-
end
-
raise unless database_transaction_rollback.is_a?(ActiveRecord::Rollback)
-
end
-
ensure
-
@transaction_joinable = last_transaction_joinable
-
-
if outside_transaction?
-
@open_transactions = 0
-
elsif transaction_open
-
decrement_open_transactions
-
begin
-
if open_transactions == 0
-
commit_db_transaction
-
commit_transaction_records
-
else
-
release_savepoint
-
save_point_records = @_current_transaction_records.pop
-
unless save_point_records.blank?
-
@_current_transaction_records.push([]) if @_current_transaction_records.empty?
-
@_current_transaction_records.last.concat(save_point_records)
-
end
-
end
-
rescue Exception => database_transaction_rollback
-
if open_transactions == 0
-
rollback_db_transaction
-
rollback_transaction_records(true)
-
else
-
rollback_to_savepoint
-
rollback_transaction_records(false)
-
end
-
raise
-
end
-
end
-
end
-
-
# Register a record with the current transaction so that its after_commit and after_rollback callbacks
-
# can be called.
-
1
def add_transaction_record(record)
-
last_batch = @_current_transaction_records.last
-
last_batch << record if last_batch
-
end
-
-
# Begins the transaction (and turns off auto-committing).
-
1
def begin_db_transaction() end
-
-
# Commits the transaction (and turns on auto-committing).
-
1
def commit_db_transaction() end
-
-
# Rolls back the transaction (and turns on auto-committing). Must be
-
# done if the transaction block raises an exception or returns false.
-
1
def rollback_db_transaction() end
-
-
# Appends +LIMIT+ and +OFFSET+ options to an SQL statement, or some SQL
-
# fragment that has the same semantics as LIMIT and OFFSET.
-
#
-
# +options+ must be a Hash which contains a +:limit+ option
-
# and an +:offset+ option.
-
#
-
# This method *modifies* the +sql+ parameter.
-
#
-
# This method is deprecated!! Stop using it!
-
#
-
# ===== Examples
-
# add_limit_offset!('SELECT * FROM suppliers', {:limit => 10, :offset => 50})
-
# generates
-
# SELECT * FROM suppliers LIMIT 10 OFFSET 50
-
1
def add_limit_offset!(sql, options)
-
if limit = options[:limit]
-
sql << " LIMIT #{sanitize_limit(limit)}"
-
end
-
if offset = options[:offset]
-
sql << " OFFSET #{offset.to_i}"
-
end
-
sql
-
end
-
1
deprecate :add_limit_offset!
-
-
1
def default_sequence_name(table, column)
-
nil
-
end
-
-
# Set the sequence to the max value of the table's column.
-
1
def reset_sequence!(table, column, sequence = nil)
-
# Do nothing by default. Implement for PostgreSQL, Oracle, ...
-
end
-
-
# Inserts the given fixture into the table. Overridden in adapters that require
-
# something beyond a simple insert (eg. Oracle).
-
1
def insert_fixture(fixture, table_name)
-
columns = Hash[columns(table_name).map { |c| [c.name, c] }]
-
-
key_list = []
-
value_list = fixture.map do |name, value|
-
key_list << quote_column_name(name)
-
quote(value, columns[name])
-
end
-
-
execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", 'Fixture Insert'
-
end
-
-
1
def empty_insert_statement_value
-
"VALUES(DEFAULT)"
-
end
-
-
1
def case_sensitive_equality_operator
-
"="
-
end
-
-
1
def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
-
"WHERE #{quoted_primary_key} IN (SELECT #{quoted_primary_key} FROM #{quoted_table_name} #{where_sql})"
-
end
-
-
# Sanitizes the given LIMIT parameter in order to prevent SQL injection.
-
#
-
# The +limit+ may be anything that can evaluate to a string via #to_s. It
-
# should look like an integer, or a comma-delimited list of integers, or
-
# an Arel SQL literal.
-
#
-
# Returns Integer and Arel::Nodes::SqlLiteral limits as is.
-
# Returns the sanitized limit parameter, either as an integer, or as a
-
# string which contains a comma-delimited list of integers.
-
1
def sanitize_limit(limit)
-
if limit.is_a?(Integer) || limit.is_a?(Arel::Nodes::SqlLiteral)
-
limit
-
elsif limit.to_s =~ /,/
-
Arel.sql limit.to_s.split(',').map{ |i| Integer(i) }.join(',')
-
else
-
Integer(limit)
-
end
-
end
-
-
# The default strategy for an UPDATE with joins is to use a subquery. This doesn't work
-
# on mysql (even when aliasing the tables), but mysql allows using JOIN directly in
-
# an UPDATE statement, so in the mysql adapters we redefine this to do that.
-
1
def join_to_update(update, select) #:nodoc:
-
subselect = select.clone
-
subselect.projections = [update.key]
-
-
update.where update.key.in(subselect)
-
end
-
-
1
protected
-
# Returns an array of record hashes with the column names as keys and
-
# column values as values.
-
1
def select(sql, name = nil, binds = [])
-
end
-
1
undef_method :select
-
-
# Returns the last auto-generated ID from the affected table.
-
1
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
-
execute(sql, name)
-
id_value
-
end
-
-
# Executes the update statement and returns the number of rows affected.
-
1
def update_sql(sql, name = nil)
-
execute(sql, name)
-
end
-
-
# Executes the delete statement and returns the number of rows affected.
-
1
def delete_sql(sql, name = nil)
-
update_sql(sql, name)
-
end
-
-
# Send a rollback message to all records after they have been rolled back. If rollback
-
# is false, only rollback records since the last save point.
-
1
def rollback_transaction_records(rollback) #:nodoc
-
if rollback
-
records = @_current_transaction_records.flatten
-
@_current_transaction_records.clear
-
else
-
records = @_current_transaction_records.pop
-
end
-
-
unless records.blank?
-
records.uniq.each do |record|
-
begin
-
record.rolledback!(rollback)
-
rescue Exception => e
-
record.logger.error(e) if record.respond_to?(:logger) && record.logger
-
end
-
end
-
end
-
end
-
-
# Send a commit message to all records after they have been committed.
-
1
def commit_transaction_records #:nodoc
-
records = @_current_transaction_records.flatten
-
@_current_transaction_records.clear
-
unless records.blank?
-
records.uniq.each do |record|
-
begin
-
record.committed!
-
rescue Exception => e
-
record.logger.error(e) if record.respond_to?(:logger) && record.logger
-
end
-
end
-
end
-
end
-
-
1
def sql_for_insert(sql, pk, id_value, sequence_name, binds)
-
[sql, binds]
-
end
-
-
1
def last_inserted_id(result)
-
row = result.rows.first
-
row && row.first
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module ConnectionAdapters # :nodoc:
-
1
module QueryCache
-
1
class << self
-
1
def included(base)
-
1
dirties_query_cache base, :insert, :update, :delete
-
end
-
-
1
def dirties_query_cache(base, *method_names)
-
1
method_names.each do |method_name|
-
3
base.class_eval <<-end_code, __FILE__, __LINE__ + 1
-
def #{method_name}(*) # def update_with_query_dirty(*args)
-
clear_query_cache if @query_cache_enabled # clear_query_cache if @query_cache_enabled
-
super # update_without_query_dirty(*args)
-
end # end
-
end_code
-
end
-
end
-
end
-
-
1
attr_reader :query_cache, :query_cache_enabled
-
-
# Enable the query cache within the block.
-
1
def cache
-
old, @query_cache_enabled = @query_cache_enabled, true
-
yield
-
ensure
-
clear_query_cache
-
@query_cache_enabled = old
-
end
-
-
1
def enable_query_cache!
-
@query_cache_enabled = true
-
end
-
-
1
def disable_query_cache!
-
@query_cache_enabled = false
-
end
-
-
# Disable the query cache within the block.
-
1
def uncached
-
old, @query_cache_enabled = @query_cache_enabled, false
-
yield
-
ensure
-
@query_cache_enabled = old
-
end
-
-
# Clears the query cache.
-
#
-
# One reason you may wish to call this method explicitly is between queries
-
# that ask the database to randomize results. Otherwise the cache would see
-
# the same SQL query and repeatedly return the same result each time, silently
-
# undermining the randomness you were expecting.
-
1
def clear_query_cache
-
@query_cache.clear
-
end
-
-
1
def select_all(arel, name = nil, binds = [])
-
if @query_cache_enabled
-
sql = to_sql(arel)
-
cache_sql(sql, binds) { super(sql, name, binds) }
-
else
-
super
-
end
-
end
-
-
1
private
-
1
def cache_sql(sql, binds)
-
result =
-
if @query_cache[sql].key?(binds)
-
ActiveSupport::Notifications.instrument("sql.active_record",
-
:sql => sql, :name => "CACHE", :connection_id => object_id)
-
@query_cache[sql][binds]
-
else
-
@query_cache[sql][binds] = yield
-
end
-
-
result.collect { |row| row.dup }
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/big_decimal/conversions'
-
-
1
module ActiveRecord
-
1
module ConnectionAdapters # :nodoc:
-
1
module Quoting
-
# Quotes the column value to help prevent
-
# {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection].
-
1
def quote(value, column = nil)
-
# records are quoted as their primary key
-
return value.quoted_id if value.respond_to?(:quoted_id)
-
-
case value
-
when String, ActiveSupport::Multibyte::Chars
-
value = value.to_s
-
return "'#{quote_string(value)}'" unless column
-
-
case column.type
-
when :binary then "'#{quote_string(column.string_to_binary(value))}'"
-
when :integer then value.to_i.to_s
-
when :float then value.to_f.to_s
-
else
-
"'#{quote_string(value)}'"
-
end
-
-
when true, false
-
if column && column.type == :integer
-
value ? '1' : '0'
-
else
-
value ? quoted_true : quoted_false
-
end
-
# BigDecimals need to be put in a non-normalized form and quoted.
-
when nil then "NULL"
-
when BigDecimal then value.to_s('F')
-
when Numeric then value.to_s
-
when Date, Time then "'#{quoted_date(value)}'"
-
when Symbol then "'#{quote_string(value.to_s)}'"
-
else
-
"'#{quote_string(YAML.dump(value))}'"
-
end
-
end
-
-
# Cast a +value+ to a type that the database understands. For example,
-
# SQLite does not understand dates, so this method will convert a Date
-
# to a String.
-
1
def type_cast(value, column)
-
return value.id if value.respond_to?(:quoted_id)
-
-
case value
-
when String, ActiveSupport::Multibyte::Chars
-
value = value.to_s
-
return value unless column
-
-
case column.type
-
when :binary then value
-
when :integer then value.to_i
-
when :float then value.to_f
-
else
-
value
-
end
-
-
when true, false
-
if column && column.type == :integer
-
value ? 1 : 0
-
else
-
value ? 't' : 'f'
-
end
-
# BigDecimals need to be put in a non-normalized form and quoted.
-
when nil then nil
-
when BigDecimal then value.to_s('F')
-
when Numeric then value
-
when Date, Time then quoted_date(value)
-
when Symbol then value.to_s
-
else
-
YAML.dump(value)
-
end
-
end
-
-
# Quotes a string, escaping any ' (single quote) and \ (backslash)
-
# characters.
-
1
def quote_string(s)
-
s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode)
-
end
-
-
# Quotes the column name. Defaults to no quoting.
-
1
def quote_column_name(column_name)
-
column_name
-
end
-
-
# Quotes the table name. Defaults to column name quoting.
-
1
def quote_table_name(table_name)
-
quote_column_name(table_name)
-
end
-
-
1
def quoted_true
-
"'t'"
-
end
-
-
1
def quoted_false
-
"'f'"
-
end
-
-
1
def quoted_date(value)
-
if value.acts_like?(:time)
-
zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
-
value.respond_to?(zone_conversion_method) ? value.send(zone_conversion_method) : value
-
else
-
value
-
end.to_s(:db)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'date'
-
1
require 'set'
-
1
require 'bigdecimal'
-
1
require 'bigdecimal/util'
-
-
1
module ActiveRecord
-
1
module ConnectionAdapters #:nodoc:
-
1
class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths) #:nodoc:
-
end
-
-
# Abstract representation of a column definition. Instances of this type
-
# are typically created by methods in TableDefinition, and added to the
-
# +columns+ attribute of said TableDefinition object, in order to be used
-
# for generating a number of table creation or table changing SQL statements.
-
1
class ColumnDefinition < Struct.new(:base, :name, :type, :limit, :precision, :scale, :default, :null) #:nodoc:
-
-
1
def string_to_binary(value)
-
value
-
end
-
-
1
def sql_type
-
base.type_to_sql(type.to_sym, limit, precision, scale) rescue type
-
end
-
-
1
def to_sql
-
column_sql = "#{base.quote_column_name(name)} #{sql_type}"
-
column_options = {}
-
column_options[:null] = null unless null.nil?
-
column_options[:default] = default unless default.nil?
-
add_column_options!(column_sql, column_options) unless type.to_sym == :primary_key
-
column_sql
-
end
-
-
1
private
-
-
1
def add_column_options!(sql, options)
-
base.add_column_options!(sql, options.merge(:column => self))
-
end
-
end
-
-
# Represents the schema of an SQL table in an abstract way. This class
-
# provides methods for manipulating the schema representation.
-
#
-
# Inside migration files, the +t+ object in +create_table+ and
-
# +change_table+ is actually of this type:
-
#
-
# class SomeMigration < ActiveRecord::Migration
-
# def self.up
-
# create_table :foo do |t|
-
# puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
-
# end
-
# end
-
#
-
# def self.down
-
# ...
-
# end
-
# end
-
#
-
# The table definitions
-
# The Columns are stored as a ColumnDefinition in the +columns+ attribute.
-
1
class TableDefinition
-
# An array of ColumnDefinition objects, representing the column changes
-
# that have been defined.
-
1
attr_accessor :columns
-
-
1
def initialize(base)
-
@columns = []
-
@base = base
-
end
-
-
1
def xml(*args)
-
raise NotImplementedError unless %w{
-
sqlite mysql mysql2
-
}.include? @base.adapter_name.downcase
-
-
options = args.extract_options!
-
column(args[0], :text, options)
-
end
-
-
# Appends a primary key definition to the table definition.
-
# Can be called multiple times, but this is probably not a good idea.
-
1
def primary_key(name)
-
column(name, :primary_key)
-
end
-
-
# Returns a ColumnDefinition for the column with name +name+.
-
1
def [](name)
-
@columns.find {|column| column.name.to_s == name.to_s}
-
end
-
-
# Instantiates a new column for the table.
-
# The +type+ parameter is normally one of the migrations native types,
-
# which is one of the following:
-
# <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
-
# <tt>:integer</tt>, <tt>:float</tt>, <tt>:decimal</tt>,
-
# <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
-
# <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>.
-
#
-
# You may use a type not in this list as long as it is supported by your
-
# database (for example, "polygon" in MySQL), but this will not be database
-
# agnostic and should usually be avoided.
-
#
-
# Available options are (none of these exists by default):
-
# * <tt>:limit</tt> -
-
# Requests a maximum column length. This is number of characters for <tt>:string</tt> and
-
# <tt>:text</tt> columns and number of bytes for <tt>:binary</tt> and <tt>:integer</tt> columns.
-
# * <tt>:default</tt> -
-
# The column's default value. Use nil for NULL.
-
# * <tt>:null</tt> -
-
# Allows or disallows +NULL+ values in the column. This option could
-
# have been named <tt>:null_allowed</tt>.
-
# * <tt>:precision</tt> -
-
# Specifies the precision for a <tt>:decimal</tt> column.
-
# * <tt>:scale</tt> -
-
# Specifies the scale for a <tt>:decimal</tt> column.
-
#
-
# For clarity's sake: the precision is the number of significant digits,
-
# while the scale is the number of digits that can be stored following
-
# the decimal point. For example, the number 123.45 has a precision of 5
-
# and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
-
# range from -999.99 to 999.99.
-
#
-
# Please be aware of different RDBMS implementations behavior with
-
# <tt>:decimal</tt> columns:
-
# * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
-
# <tt>:precision</tt>, and makes no comments about the requirements of
-
# <tt>:precision</tt>.
-
# * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
-
# Default is (10,0).
-
# * PostgreSQL: <tt>:precision</tt> [1..infinity],
-
# <tt>:scale</tt> [0..infinity]. No default.
-
# * SQLite2: Any <tt>:precision</tt> and <tt>:scale</tt> may be used.
-
# Internal storage as strings. No default.
-
# * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
-
# but the maximum supported <tt>:precision</tt> is 16. No default.
-
# * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
-
# Default is (38,0).
-
# * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
-
# Default unknown.
-
# * Firebird: <tt>:precision</tt> [1..18], <tt>:scale</tt> [0..18].
-
# Default (9,0). Internal types NUMERIC and DECIMAL have different
-
# storage rules, decimal being better.
-
# * FrontBase?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
-
# Default (38,0). WARNING Max <tt>:precision</tt>/<tt>:scale</tt> for
-
# NUMERIC is 19, and DECIMAL is 38.
-
# * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
-
# Default (38,0).
-
# * Sybase: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
-
# Default (38,0).
-
# * OpenBase?: Documentation unclear. Claims storage in <tt>double</tt>.
-
#
-
# This method returns <tt>self</tt>.
-
#
-
# == Examples
-
# # Assuming +td+ is an instance of TableDefinition
-
# td.column(:granted, :boolean)
-
# # granted BOOLEAN
-
#
-
# td.column(:picture, :binary, :limit => 2.megabytes)
-
# # => picture BLOB(2097152)
-
#
-
# td.column(:sales_stage, :string, :limit => 20, :default => 'new', :null => false)
-
# # => sales_stage VARCHAR(20) DEFAULT 'new' NOT NULL
-
#
-
# td.column(:bill_gates_money, :decimal, :precision => 15, :scale => 2)
-
# # => bill_gates_money DECIMAL(15,2)
-
#
-
# td.column(:sensor_reading, :decimal, :precision => 30, :scale => 20)
-
# # => sensor_reading DECIMAL(30,20)
-
#
-
# # While <tt>:scale</tt> defaults to zero on most databases, it
-
# # probably wouldn't hurt to include it.
-
# td.column(:huge_integer, :decimal, :precision => 30)
-
# # => huge_integer DECIMAL(30)
-
#
-
# # Defines a column with a database-specific type.
-
# td.column(:foo, 'polygon')
-
# # => foo polygon
-
#
-
# == Short-hand examples
-
#
-
# Instead of calling +column+ directly, you can also work with the short-hand definitions for the default types.
-
# They use the type as the method name instead of as a parameter and allow for multiple columns to be defined
-
# in a single statement.
-
#
-
# What can be written like this with the regular calls to column:
-
#
-
# create_table "products", :force => true do |t|
-
# t.column "shop_id", :integer
-
# t.column "creator_id", :integer
-
# t.column "name", :string, :default => "Untitled"
-
# t.column "value", :string, :default => "Untitled"
-
# t.column "created_at", :datetime
-
# t.column "updated_at", :datetime
-
# end
-
#
-
# Can also be written as follows using the short-hand:
-
#
-
# create_table :products do |t|
-
# t.integer :shop_id, :creator_id
-
# t.string :name, :value, :default => "Untitled"
-
# t.timestamps
-
# end
-
#
-
# There's a short-hand method for each of the type values declared at the top. And then there's
-
# TableDefinition#timestamps that'll add +created_at+ and +updated_at+ as datetimes.
-
#
-
# TableDefinition#references will add an appropriately-named _id column, plus a corresponding _type
-
# column if the <tt>:polymorphic</tt> option is supplied. If <tt>:polymorphic</tt> is a hash of
-
# options, these will be used when creating the <tt>_type</tt> column. So what can be written like this:
-
#
-
# create_table :taggings do |t|
-
# t.integer :tag_id, :tagger_id, :taggable_id
-
# t.string :tagger_type
-
# t.string :taggable_type, :default => 'Photo'
-
# end
-
#
-
# Can also be written as follows using references:
-
#
-
# create_table :taggings do |t|
-
# t.references :tag
-
# t.references :tagger, :polymorphic => true
-
# t.references :taggable, :polymorphic => { :default => 'Photo' }
-
# end
-
1
def column(name, type, options = {})
-
column = self[name] || ColumnDefinition.new(@base, name, type)
-
if options[:limit]
-
column.limit = options[:limit]
-
elsif native[type.to_sym].is_a?(Hash)
-
column.limit = native[type.to_sym][:limit]
-
end
-
column.precision = options[:precision]
-
column.scale = options[:scale]
-
column.default = options[:default]
-
column.null = options[:null]
-
@columns << column unless @columns.include? column
-
self
-
end
-
-
1
%w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
-
11
class_eval <<-EOV, __FILE__, __LINE__ + 1
-
def #{column_type}(*args) # def string(*args)
-
options = args.extract_options! # options = args.extract_options!
-
column_names = args # column_names = args
-
#
-
column_names.each { |name| column(name, '#{column_type}', options) } # column_names.each { |name| column(name, 'string', options) }
-
end # end
-
EOV
-
end
-
-
# Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
-
# <tt>:updated_at</tt> to the table.
-
1
def timestamps(*args)
-
options = args.extract_options!
-
column(:created_at, :datetime, options)
-
column(:updated_at, :datetime, options)
-
end
-
-
1
def references(*args)
-
options = args.extract_options!
-
polymorphic = options.delete(:polymorphic)
-
args.each do |col|
-
column("#{col}_id", :integer, options)
-
column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) unless polymorphic.nil?
-
end
-
end
-
1
alias :belongs_to :references
-
-
# Returns a String whose contents are the column definitions
-
# concatenated together. This string can then be prepended and appended to
-
# to generate the final SQL to create the table.
-
1
def to_sql
-
@columns.map { |c| c.to_sql } * ', '
-
end
-
-
1
private
-
1
def native
-
@base.native_database_types
-
end
-
end
-
-
# Represents an SQL table in an abstract way for updating a table.
-
# Also see TableDefinition and SchemaStatements#create_table
-
#
-
# Available transformations are:
-
#
-
# change_table :table do |t|
-
# t.column
-
# t.index
-
# t.timestamps
-
# t.change
-
# t.change_default
-
# t.rename
-
# t.references
-
# t.belongs_to
-
# t.string
-
# t.text
-
# t.integer
-
# t.float
-
# t.decimal
-
# t.datetime
-
# t.timestamp
-
# t.time
-
# t.date
-
# t.binary
-
# t.boolean
-
# t.remove
-
# t.remove_references
-
# t.remove_belongs_to
-
# t.remove_index
-
# t.remove_timestamps
-
# end
-
#
-
1
class Table
-
1
def initialize(table_name, base)
-
@table_name = table_name
-
@base = base
-
end
-
-
# Adds a new column to the named table.
-
# See TableDefinition#column for details of the options you can use.
-
# ===== Example
-
# ====== Creating a simple column
-
# t.column(:name, :string)
-
1
def column(column_name, type, options = {})
-
@base.add_column(@table_name, column_name, type, options)
-
end
-
-
# Checks to see if a column exists. See SchemaStatements#column_exists?
-
1
def column_exists?(column_name, type = nil, options = {})
-
@base.column_exists?(@table_name, column_name, type, options)
-
end
-
-
# Adds a new index to the table. +column_name+ can be a single Symbol, or
-
# an Array of Symbols. See SchemaStatements#add_index
-
#
-
# ===== Examples
-
# ====== Creating a simple index
-
# t.index(:name)
-
# ====== Creating a unique index
-
# t.index([:branch_id, :party_id], :unique => true)
-
# ====== Creating a named index
-
# t.index([:branch_id, :party_id], :unique => true, :name => 'by_branch_party')
-
1
def index(column_name, options = {})
-
@base.add_index(@table_name, column_name, options)
-
end
-
-
# Checks to see if an index exists. See SchemaStatements#index_exists?
-
1
def index_exists?(column_name, options = {})
-
@base.index_exists?(@table_name, column_name, options)
-
end
-
-
# Adds timestamps (+created_at+ and +updated_at+) columns to the table. See SchemaStatements#add_timestamps
-
# ===== Example
-
# t.timestamps
-
1
def timestamps
-
@base.add_timestamps(@table_name)
-
end
-
-
# Changes the column's definition according to the new options.
-
# See TableDefinition#column for details of the options you can use.
-
# ===== Examples
-
# t.change(:name, :string, :limit => 80)
-
# t.change(:description, :text)
-
1
def change(column_name, type, options = {})
-
@base.change_column(@table_name, column_name, type, options)
-
end
-
-
# Sets a new default value for a column. See SchemaStatements#change_column_default
-
# ===== Examples
-
# t.change_default(:qualification, 'new')
-
# t.change_default(:authorized, 1)
-
1
def change_default(column_name, default)
-
@base.change_column_default(@table_name, column_name, default)
-
end
-
-
# Removes the column(s) from the table definition.
-
# ===== Examples
-
# t.remove(:qualification)
-
# t.remove(:qualification, :experience)
-
1
def remove(*column_names)
-
@base.remove_column(@table_name, column_names)
-
end
-
-
# Removes the given index from the table.
-
#
-
# ===== Examples
-
# ====== Remove the index_table_name_on_column in the table_name table
-
# t.remove_index :column
-
# ====== Remove the index named index_table_name_on_branch_id in the table_name table
-
# t.remove_index :column => :branch_id
-
# ====== Remove the index named index_table_name_on_branch_id_and_party_id in the table_name table
-
# t.remove_index :column => [:branch_id, :party_id]
-
# ====== Remove the index named by_branch_party in the table_name table
-
# t.remove_index :name => :by_branch_party
-
1
def remove_index(options = {})
-
@base.remove_index(@table_name, options)
-
end
-
-
# Removes the timestamp columns (+created_at+ and +updated_at+) from the table.
-
# ===== Example
-
# t.remove_timestamps
-
1
def remove_timestamps
-
@base.remove_timestamps(@table_name)
-
end
-
-
# Renames a column.
-
# ===== Example
-
# t.rename(:description, :name)
-
1
def rename(column_name, new_column_name)
-
@base.rename_column(@table_name, column_name, new_column_name)
-
end
-
-
# Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided.
-
# <tt>references</tt> and <tt>belongs_to</tt> are acceptable.
-
# ===== Examples
-
# t.references(:goat)
-
# t.references(:goat, :polymorphic => true)
-
# t.belongs_to(:goat)
-
1
def references(*args)
-
options = args.extract_options!
-
polymorphic = options.delete(:polymorphic)
-
args.each do |col|
-
@base.add_column(@table_name, "#{col}_id", :integer, options)
-
@base.add_column(@table_name, "#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) unless polymorphic.nil?
-
end
-
end
-
1
alias :belongs_to :references
-
-
# Removes a reference. Optionally removes a +type+ column.
-
# <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
-
# ===== Examples
-
# t.remove_references(:goat)
-
# t.remove_references(:goat, :polymorphic => true)
-
# t.remove_belongs_to(:goat)
-
1
def remove_references(*args)
-
options = args.extract_options!
-
polymorphic = options.delete(:polymorphic)
-
args.each do |col|
-
@base.remove_column(@table_name, "#{col}_id")
-
@base.remove_column(@table_name, "#{col}_type") unless polymorphic.nil?
-
end
-
end
-
1
alias :remove_belongs_to :remove_references
-
-
# Adds a column or columns of a specified type
-
# ===== Examples
-
# t.string(:goat)
-
# t.string(:goat, :sheep)
-
1
%w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
-
11
class_eval <<-EOV, __FILE__, __LINE__ + 1
-
def #{column_type}(*args) # def string(*args)
-
options = args.extract_options! # options = args.extract_options!
-
column_names = args # column_names = args
-
#
-
column_names.each do |name| # column_names.each do |name|
-
column = ColumnDefinition.new(@base, name, '#{column_type}') # column = ColumnDefinition.new(@base, name, 'string')
-
if options[:limit] # if options[:limit]
-
column.limit = options[:limit] # column.limit = options[:limit]
-
elsif native['#{column_type}'.to_sym].is_a?(Hash) # elsif native['string'.to_sym].is_a?(Hash)
-
column.limit = native['#{column_type}'.to_sym][:limit] # column.limit = native['string'.to_sym][:limit]
-
end # end
-
column.precision = options[:precision] # column.precision = options[:precision]
-
column.scale = options[:scale] # column.scale = options[:scale]
-
column.default = options[:default] # column.default = options[:default]
-
column.null = options[:null] # column.null = options[:null]
-
@base.add_column(@table_name, name, column.sql_type, options) # @base.add_column(@table_name, name, column.sql_type, options)
-
end # end
-
end # end
-
EOV
-
end
-
-
1
private
-
1
def native
-
@base.native_database_types
-
end
-
end
-
-
end
-
end
-
-
1
require 'active_support/core_ext/array/wrap'
-
-
1
module ActiveRecord
-
1
module ConnectionAdapters # :nodoc:
-
1
module SchemaStatements
-
# Returns a Hash of mappings from the abstract data types to the native
-
# database types. See TableDefinition#column for details on the recognized
-
# abstract data types.
-
1
def native_database_types
-
{}
-
end
-
-
# Truncates a table alias according to the limits of the current adapter.
-
1
def table_alias_for(table_name)
-
table_name[0...table_alias_length].gsub(/\./, '_')
-
end
-
-
# def tables(name = nil) end
-
-
# Checks to see if the table +table_name+ exists on the database.
-
#
-
# === Example
-
# table_exists?(:developers)
-
1
def table_exists?(table_name)
-
3
tables.include?(table_name.to_s)
-
end
-
-
# Returns an array of indexes for the given table.
-
# def indexes(table_name, name = nil) end
-
-
# Checks to see if an index exists on a table for a given index definition.
-
#
-
# === Examples
-
# # Check an index exists
-
# index_exists?(:suppliers, :company_id)
-
#
-
# # Check an index on multiple columns exists
-
# index_exists?(:suppliers, [:company_id, :company_type])
-
#
-
# # Check a unique index exists
-
# index_exists?(:suppliers, :company_id, :unique => true)
-
#
-
# # Check an index with a custom name exists
-
# index_exists?(:suppliers, :company_id, :name => "idx_company_id"
-
1
def index_exists?(table_name, column_name, options = {})
-
column_names = Array.wrap(column_name)
-
index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, :column => column_names)
-
if options[:unique]
-
indexes(table_name).any?{ |i| i.unique && i.name == index_name }
-
else
-
indexes(table_name).any?{ |i| i.name == index_name }
-
end
-
end
-
-
# Returns an array of Column objects for the table specified by +table_name+.
-
# See the concrete implementation for details on the expected parameter values.
-
1
def columns(table_name, name = nil) end
-
-
# Checks to see if a column exists in a given table.
-
#
-
# === Examples
-
# # Check a column exists
-
# column_exists?(:suppliers, :name)
-
#
-
# # Check a column exists of a particular type
-
# column_exists?(:suppliers, :name, :string)
-
#
-
# # Check a column exists with a specific definition
-
# column_exists?(:suppliers, :name, :string, :limit => 100)
-
1
def column_exists?(table_name, column_name, type = nil, options = {})
-
columns(table_name).any?{ |c| c.name == column_name.to_s &&
-
(!type || c.type == type) &&
-
(!options[:limit] || c.limit == options[:limit]) &&
-
(!options[:precision] || c.precision == options[:precision]) &&
-
(!options[:scale] || c.scale == options[:scale]) }
-
end
-
-
# Creates a new table with the name +table_name+. +table_name+ may either
-
# be a String or a Symbol.
-
#
-
# There are two ways to work with +create_table+. You can use the block
-
# form or the regular form, like this:
-
#
-
# === Block form
-
# # create_table() passes a TableDefinition object to the block.
-
# # This form will not only create the table, but also columns for the
-
# # table.
-
#
-
# create_table(:suppliers) do |t|
-
# t.column :name, :string, :limit => 60
-
# # Other fields here
-
# end
-
#
-
# === Block form, with shorthand
-
# # You can also use the column types as method calls, rather than calling the column method.
-
# create_table(:suppliers) do |t|
-
# t.string :name, :limit => 60
-
# # Other fields here
-
# end
-
#
-
# === Regular form
-
# # Creates a table called 'suppliers' with no columns.
-
# create_table(:suppliers)
-
# # Add a column to 'suppliers'.
-
# add_column(:suppliers, :name, :string, {:limit => 60})
-
#
-
# The +options+ hash can include the following keys:
-
# [<tt>:id</tt>]
-
# Whether to automatically add a primary key column. Defaults to true.
-
# Join tables for +has_and_belongs_to_many+ should set it to false.
-
# [<tt>:primary_key</tt>]
-
# The name of the primary key, if one is to be added automatically.
-
# Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
-
#
-
# Also note that this just sets the primary key in the table. You additionally
-
# need to configure the primary key in the model via the +set_primary_key+ macro.
-
# Models do NOT auto-detect the primary key from their table definition.
-
#
-
# [<tt>:options</tt>]
-
# Any extra options you want appended to the table definition.
-
# [<tt>:temporary</tt>]
-
# Make a temporary table.
-
# [<tt>:force</tt>]
-
# Set to true to drop the table before creating it.
-
# Defaults to false.
-
#
-
# ===== Examples
-
# ====== Add a backend specific option to the generated SQL (MySQL)
-
# create_table(:suppliers, :options => 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
-
# generates:
-
# CREATE TABLE suppliers (
-
# id int(11) DEFAULT NULL auto_increment PRIMARY KEY
-
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8
-
#
-
# ====== Rename the primary key column
-
# create_table(:objects, :primary_key => 'guid') do |t|
-
# t.column :name, :string, :limit => 80
-
# end
-
# generates:
-
# CREATE TABLE objects (
-
# guid int(11) DEFAULT NULL auto_increment PRIMARY KEY,
-
# name varchar(80)
-
# )
-
#
-
# ====== Do not add a primary key column
-
# create_table(:categories_suppliers, :id => false) do |t|
-
# t.column :category_id, :integer
-
# t.column :supplier_id, :integer
-
# end
-
# generates:
-
# CREATE TABLE categories_suppliers (
-
# category_id int,
-
# supplier_id int
-
# )
-
#
-
# See also TableDefinition#column for details on how to create columns.
-
1
def create_table(table_name, options = {})
-
td = table_definition
-
td.primary_key(options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false
-
-
yield td if block_given?
-
-
if options[:force] && table_exists?(table_name)
-
drop_table(table_name, options)
-
end
-
-
create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
-
create_sql << "#{quote_table_name(table_name)} ("
-
create_sql << td.to_sql
-
create_sql << ") #{options[:options]}"
-
execute create_sql
-
end
-
-
# A block for changing columns in +table+.
-
#
-
# === Example
-
# # change_table() yields a Table instance
-
# change_table(:suppliers) do |t|
-
# t.column :name, :string, :limit => 60
-
# # Other column alterations here
-
# end
-
#
-
# The +options+ hash can include the following keys:
-
# [<tt>:bulk</tt>]
-
# Set this to true to make this a bulk alter query, such as
-
# ALTER TABLE `users` ADD COLUMN age INT(11), ADD COLUMN birthdate DATETIME ...
-
#
-
# Defaults to false.
-
#
-
# ===== Examples
-
# ====== Add a column
-
# change_table(:suppliers) do |t|
-
# t.column :name, :string, :limit => 60
-
# end
-
#
-
# ====== Add 2 integer columns
-
# change_table(:suppliers) do |t|
-
# t.integer :width, :height, :null => false, :default => 0
-
# end
-
#
-
# ====== Add created_at/updated_at columns
-
# change_table(:suppliers) do |t|
-
# t.timestamps
-
# end
-
#
-
# ====== Add a foreign key column
-
# change_table(:suppliers) do |t|
-
# t.references :company
-
# end
-
#
-
# Creates a <tt>company_id(integer)</tt> column
-
#
-
# ====== Add a polymorphic foreign key column
-
# change_table(:suppliers) do |t|
-
# t.belongs_to :company, :polymorphic => true
-
# end
-
#
-
# Creates <tt>company_type(varchar)</tt> and <tt>company_id(integer)</tt> columns
-
#
-
# ====== Remove a column
-
# change_table(:suppliers) do |t|
-
# t.remove :company
-
# end
-
#
-
# ====== Remove several columns
-
# change_table(:suppliers) do |t|
-
# t.remove :company_id
-
# t.remove :width, :height
-
# end
-
#
-
# ====== Remove an index
-
# change_table(:suppliers) do |t|
-
# t.remove_index :company_id
-
# end
-
#
-
# See also Table for details on
-
# all of the various column transformation
-
1
def change_table(table_name, options = {})
-
if supports_bulk_alter? && options[:bulk]
-
recorder = ActiveRecord::Migration::CommandRecorder.new(self)
-
yield Table.new(table_name, recorder)
-
bulk_change_table(table_name, recorder.commands)
-
else
-
yield Table.new(table_name, self)
-
end
-
end
-
-
# Renames a table.
-
# ===== Example
-
# rename_table('octopuses', 'octopi')
-
1
def rename_table(table_name, new_name)
-
raise NotImplementedError, "rename_table is not implemented"
-
end
-
-
# Drops a table from the database.
-
1
def drop_table(table_name, options = {})
-
execute "DROP TABLE #{quote_table_name(table_name)}"
-
end
-
-
# Adds a new column to the named table.
-
# See TableDefinition#column for details of the options you can use.
-
1
def add_column(table_name, column_name, type, options = {})
-
add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
-
add_column_options!(add_column_sql, options)
-
execute(add_column_sql)
-
end
-
-
# Removes the column(s) from the table definition.
-
# ===== Examples
-
# remove_column(:suppliers, :qualification)
-
# remove_columns(:suppliers, :qualification, :experience)
-
1
def remove_column(table_name, *column_names)
-
columns_for_remove(table_name, *column_names).each {|column_name| execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{column_name}" }
-
end
-
1
alias :remove_columns :remove_column
-
-
# Changes the column's definition according to the new options.
-
# See TableDefinition#column for details of the options you can use.
-
# ===== Examples
-
# change_column(:suppliers, :name, :string, :limit => 80)
-
# change_column(:accounts, :description, :text)
-
1
def change_column(table_name, column_name, type, options = {})
-
raise NotImplementedError, "change_column is not implemented"
-
end
-
-
# Sets a new default value for a column.
-
# ===== Examples
-
# change_column_default(:suppliers, :qualification, 'new')
-
# change_column_default(:accounts, :authorized, 1)
-
# change_column_default(:users, :email, nil)
-
1
def change_column_default(table_name, column_name, default)
-
raise NotImplementedError, "change_column_default is not implemented"
-
end
-
-
# Renames a column.
-
# ===== Example
-
# rename_column(:suppliers, :description, :name)
-
1
def rename_column(table_name, column_name, new_column_name)
-
raise NotImplementedError, "rename_column is not implemented"
-
end
-
-
# Adds a new index to the table. +column_name+ can be a single Symbol, or
-
# an Array of Symbols.
-
#
-
# The index will be named after the table and the first column name,
-
# unless you pass <tt>:name</tt> as an option.
-
#
-
# When creating an index on multiple columns, the first column is used as a name
-
# for the index. For example, when you specify an index on two columns
-
# [<tt>:first</tt>, <tt>:last</tt>], the DBMS creates an index for both columns as well as an
-
# index for the first column <tt>:first</tt>. Using just the first name for this index
-
# makes sense, because you will never have to create a singular index with this
-
# name.
-
#
-
# ===== Examples
-
#
-
# ====== Creating a simple index
-
# add_index(:suppliers, :name)
-
# generates
-
# CREATE INDEX suppliers_name_index ON suppliers(name)
-
#
-
# ====== Creating a unique index
-
# add_index(:accounts, [:branch_id, :party_id], :unique => true)
-
# generates
-
# CREATE UNIQUE INDEX accounts_branch_id_party_id_index ON accounts(branch_id, party_id)
-
#
-
# ====== Creating a named index
-
# add_index(:accounts, [:branch_id, :party_id], :unique => true, :name => 'by_branch_party')
-
# generates
-
# CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
-
#
-
# ====== Creating an index with specific key length
-
# add_index(:accounts, :name, :name => 'by_name', :length => 10)
-
# generates
-
# CREATE INDEX by_name ON accounts(name(10))
-
#
-
# add_index(:accounts, [:name, :surname], :name => 'by_name_surname', :length => {:name => 10, :surname => 15})
-
# generates
-
# CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
-
#
-
# Note: SQLite doesn't support index length
-
1
def add_index(table_name, column_name, options = {})
-
index_name, index_type, index_columns = add_index_options(table_name, column_name, options)
-
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})"
-
end
-
-
# Remove the given index from the table.
-
#
-
# Remove the index_accounts_on_column in the accounts table.
-
# remove_index :accounts, :column
-
# Remove the index named index_accounts_on_branch_id in the accounts table.
-
# remove_index :accounts, :column => :branch_id
-
# Remove the index named index_accounts_on_branch_id_and_party_id in the accounts table.
-
# remove_index :accounts, :column => [:branch_id, :party_id]
-
# Remove the index named by_branch_party in the accounts table.
-
# remove_index :accounts, :name => :by_branch_party
-
1
def remove_index(table_name, options = {})
-
remove_index!(table_name, index_name_for_remove(table_name, options))
-
end
-
-
1
def remove_index!(table_name, index_name) #:nodoc:
-
execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
-
end
-
-
# Rename an index.
-
#
-
# Rename the index_people_on_last_name index to index_users_on_last_name
-
# rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
-
1
def rename_index(table_name, old_name, new_name)
-
# this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance)
-
old_index_def = indexes(table_name).detect { |i| i.name == old_name }
-
return unless old_index_def
-
remove_index(table_name, :name => old_name)
-
add_index(table_name, old_index_def.columns, :name => new_name, :unique => old_index_def.unique)
-
end
-
-
1
def index_name(table_name, options) #:nodoc:
-
if Hash === options # legacy support
-
if options[:column]
-
"index_#{table_name}_on_#{Array.wrap(options[:column]) * '_and_'}"
-
elsif options[:name]
-
options[:name]
-
else
-
raise ArgumentError, "You must specify the index name"
-
end
-
else
-
index_name(table_name, :column => options)
-
end
-
end
-
-
# Verify the existence of an index with a given name.
-
#
-
# The default argument is returned if the underlying implementation does not define the indexes method,
-
# as there's no way to determine the correct answer in that case.
-
1
def index_name_exists?(table_name, index_name, default)
-
return default unless respond_to?(:indexes)
-
index_name = index_name.to_s
-
indexes(table_name).detect { |i| i.name == index_name }
-
end
-
-
# Returns a string of <tt>CREATE TABLE</tt> SQL statement(s) for recreating the
-
# entire structure of the database.
-
1
def structure_dump
-
end
-
-
1
def dump_schema_information #:nodoc:
-
sm_table = ActiveRecord::Migrator.schema_migrations_table_name
-
migrated = select_values("SELECT version FROM #{sm_table}")
-
migrated.map { |v| "INSERT INTO #{sm_table} (version) VALUES ('#{v}');" }.join("\n\n")
-
end
-
-
# Should not be called normally, but this operation is non-destructive.
-
# The migrations module handles this automatically.
-
1
def initialize_schema_migrations_table
-
sm_table = ActiveRecord::Migrator.schema_migrations_table_name
-
-
unless table_exists?(sm_table)
-
create_table(sm_table, :id => false) do |schema_migrations_table|
-
schema_migrations_table.column :version, :string, :null => false
-
end
-
add_index sm_table, :version, :unique => true,
-
:name => "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}"
-
-
# Backwards-compatibility: if we find schema_info, assume we've
-
# migrated up to that point:
-
si_table = Base.table_name_prefix + 'schema_info' + Base.table_name_suffix
-
-
if table_exists?(si_table)
-
-
old_version = select_value("SELECT version FROM #{quote_table_name(si_table)}").to_i
-
assume_migrated_upto_version(old_version)
-
drop_table(si_table)
-
end
-
end
-
end
-
-
1
def assume_migrated_upto_version(version, migrations_paths = ActiveRecord::Migrator.migrations_paths)
-
migrations_paths = Array.wrap(migrations_paths)
-
version = version.to_i
-
sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
-
-
migrated = select_values("SELECT version FROM #{sm_table}").map { |v| v.to_i }
-
paths = migrations_paths.map {|p| "#{p}/[0-9]*_*.rb" }
-
versions = Dir[*paths].map do |filename|
-
filename.split('/').last.split('_').first.to_i
-
end
-
-
unless migrated.include?(version)
-
execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')"
-
end
-
-
inserted = Set.new
-
(versions - migrated).each do |v|
-
if inserted.include?(v)
-
raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict."
-
elsif v < version
-
execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')"
-
inserted << v
-
end
-
end
-
end
-
-
1
def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
-
if native = native_database_types[type.to_sym]
-
column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
-
-
if type == :decimal # ignore limit, use precision and scale
-
scale ||= native[:scale]
-
-
if precision ||= native[:precision]
-
if scale
-
column_type_sql << "(#{precision},#{scale})"
-
else
-
column_type_sql << "(#{precision})"
-
end
-
elsif scale
-
raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale if specified"
-
end
-
-
elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
-
column_type_sql << "(#{limit})"
-
end
-
-
column_type_sql
-
else
-
type
-
end
-
end
-
-
1
def add_column_options!(sql, options) #:nodoc:
-
sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options)
-
# must explicitly check for :null to allow change_column to work on migrations
-
if options[:null] == false
-
sql << " NOT NULL"
-
end
-
end
-
-
# SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
-
# Both PostgreSQL and Oracle overrides this for custom DISTINCT syntax.
-
#
-
# distinct("posts.id", "posts.created_at desc")
-
1
def distinct(columns, order_by)
-
"DISTINCT #{columns}"
-
end
-
-
# Adds timestamps (created_at and updated_at) columns to the named table.
-
# ===== Examples
-
# add_timestamps(:suppliers)
-
1
def add_timestamps(table_name)
-
add_column table_name, :created_at, :datetime
-
add_column table_name, :updated_at, :datetime
-
end
-
-
# Removes the timestamp columns (created_at and updated_at) from the table definition.
-
# ===== Examples
-
# remove_timestamps(:suppliers)
-
1
def remove_timestamps(table_name)
-
remove_column table_name, :updated_at
-
remove_column table_name, :created_at
-
end
-
-
1
protected
-
# Overridden by the mysql adapter for supporting index lengths
-
1
def quoted_columns_for_index(column_names, options = {})
-
column_names.map {|name| quote_column_name(name) }
-
end
-
-
1
def options_include_default?(options)
-
options.include?(:default) && !(options[:null] == false && options[:default].nil?)
-
end
-
-
1
def add_index_options(table_name, column_name, options = {})
-
column_names = Array.wrap(column_name)
-
index_name = index_name(table_name, :column => column_names)
-
-
if Hash === options # legacy support, since this param was a string
-
index_type = options[:unique] ? "UNIQUE" : ""
-
index_name = options[:name].to_s if options.key?(:name)
-
else
-
index_type = options
-
end
-
-
if index_name.length > index_name_length
-
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters"
-
end
-
if index_name_exists?(table_name, index_name, false)
-
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
-
end
-
index_columns = quoted_columns_for_index(column_names, options).join(", ")
-
-
[index_name, index_type, index_columns]
-
end
-
-
1
def index_name_for_remove(table_name, options = {})
-
index_name = index_name(table_name, options)
-
-
unless index_name_exists?(table_name, index_name, true)
-
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
-
end
-
-
index_name
-
end
-
-
1
def columns_for_remove(table_name, *column_names)
-
column_names = column_names.flatten
-
-
raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.blank?
-
column_names.map {|column_name| quote_column_name(column_name) }
-
end
-
-
1
private
-
1
def table_definition
-
TableDefinition.new(self)
-
end
-
end
-
end
-
end
-
1
require 'date'
-
1
require 'bigdecimal'
-
1
require 'bigdecimal/util'
-
1
require 'active_support/core_ext/benchmark'
-
1
require 'active_support/deprecation'
-
-
# TODO: Autoload these files
-
1
require 'active_record/connection_adapters/column'
-
1
require 'active_record/connection_adapters/abstract/schema_definitions'
-
1
require 'active_record/connection_adapters/abstract/schema_statements'
-
1
require 'active_record/connection_adapters/abstract/database_statements'
-
1
require 'active_record/connection_adapters/abstract/quoting'
-
1
require 'active_record/connection_adapters/abstract/connection_pool'
-
1
require 'active_record/connection_adapters/abstract/connection_specification'
-
1
require 'active_record/connection_adapters/abstract/query_cache'
-
1
require 'active_record/connection_adapters/abstract/database_limits'
-
1
require 'active_record/result'
-
-
1
module ActiveRecord
-
1
module ConnectionAdapters # :nodoc:
-
# Active Record supports multiple database systems. AbstractAdapter and
-
# related classes form the abstraction layer which makes this possible.
-
# An AbstractAdapter represents a connection to a database, and provides an
-
# abstract interface for database-specific functionality such as establishing
-
# a connection, escaping values, building the right SQL fragments for ':offset'
-
# and ':limit' options, etc.
-
#
-
# All the concrete database adapters follow the interface laid down in this class.
-
# ActiveRecord::Base.connection returns an AbstractAdapter object, which
-
# you can use.
-
#
-
# Most of the methods in the adapter are useful during migrations. Most
-
# notably, the instance methods provided by SchemaStatement are very useful.
-
1
class AbstractAdapter
-
1
include Quoting, DatabaseStatements, SchemaStatements
-
1
include DatabaseLimits
-
1
include QueryCache
-
1
include ActiveSupport::Callbacks
-
-
1
define_callbacks :checkout, :checkin
-
-
1
attr_accessor :visitor
-
-
1
def initialize(connection, logger = nil) #:nodoc:
-
1
@active = nil
-
1
@connection, @logger = connection, logger
-
1
@query_cache_enabled = false
-
1
@query_cache = Hash.new { |h,sql| h[sql] = {} }
-
1
@instrumenter = ActiveSupport::Notifications.instrumenter
-
1
@visitor = nil
-
end
-
-
# Returns a visitor instance for this adaptor, which conforms to the Arel::ToSql interface
-
1
def self.visitor_for(pool) # :nodoc:
-
adapter = pool.spec.config[:adapter]
-
-
if Arel::Visitors::VISITORS[adapter]
-
ActiveSupport::Deprecation.warn(
-
"Arel::Visitors::VISITORS is deprecated and will be removed. Database adapters " \
-
"should define a visitor_for method which returns the appropriate visitor for " \
-
"the database. For example, MysqlAdapter.visitor_for(pool) returns " \
-
"Arel::Visitors::MySQL.new(pool)."
-
)
-
-
Arel::Visitors::VISITORS[adapter].new(pool)
-
else
-
Arel::Visitors::ToSql.new(pool)
-
end
-
end
-
-
# Returns the human-readable name of the adapter. Use mixed case - one
-
# can always use downcase if needed.
-
1
def adapter_name
-
'Abstract'
-
end
-
-
# Does this adapter support migrations? Backend specific, as the
-
# abstract adapter always returns +false+.
-
1
def supports_migrations?
-
false
-
end
-
-
# Can this adapter determine the primary key for tables not attached
-
# to an Active Record class, such as join tables? Backend specific, as
-
# the abstract adapter always returns +false+.
-
1
def supports_primary_key?
-
false
-
end
-
-
# Does this adapter support using DISTINCT within COUNT? This is +true+
-
# for all adapters except sqlite.
-
1
def supports_count_distinct?
-
true
-
end
-
-
# Does this adapter support DDL rollbacks in transactions? That is, would
-
# CREATE TABLE or ALTER TABLE get rolled back by a transaction? PostgreSQL,
-
# SQL Server, and others support this. MySQL and others do not.
-
1
def supports_ddl_transactions?
-
false
-
end
-
-
1
def supports_bulk_alter?
-
false
-
end
-
-
# Does this adapter support savepoints? PostgreSQL and MySQL do,
-
# SQLite < 3.6.8 does not.
-
1
def supports_savepoints?
-
false
-
end
-
-
# Should primary key values be selected from their corresponding
-
# sequence before the insert statement? If true, next_sequence_value
-
# is called before each insert to set the record's primary key.
-
# This is false for all adapters but Firebird.
-
1
def prefetch_primary_key?(table_name = nil)
-
false
-
end
-
-
# QUOTING ==================================================
-
-
# Override to return the quoted table name. Defaults to column quoting.
-
1
def quote_table_name(name)
-
5
quote_column_name(name)
-
end
-
-
# Returns a bind substitution value given a +column+ and list of current
-
# +binds+
-
1
def substitute_at(column, index)
-
Arel.sql '?'
-
end
-
-
# REFERENTIAL INTEGRITY ====================================
-
-
# Override to turn off referential integrity while executing <tt>&block</tt>.
-
1
def disable_referential_integrity
-
yield
-
end
-
-
# CONNECTION MANAGEMENT ====================================
-
-
# Checks whether the connection to the database is still active. This includes
-
# checking whether the database is actually capable of responding, i.e. whether
-
# the connection isn't stale.
-
1
def active?
-
1
@active != false
-
end
-
-
# Disconnects from the database if already connected, and establishes a
-
# new connection with the database.
-
1
def reconnect!
-
@active = true
-
end
-
-
# Disconnects from the database if already connected. Otherwise, this
-
# method does nothing.
-
1
def disconnect!
-
@active = false
-
end
-
-
# Reset the state of this connection, directing the DBMS to clear
-
# transactions and other connection-related server-side state. Usually a
-
# database-dependent operation.
-
#
-
# The default implementation does nothing; the implementation should be
-
# overridden by concrete adapters.
-
1
def reset!
-
# this should be overridden by concrete adapters
-
end
-
-
###
-
# Clear any caching the database adapter may be doing, for example
-
# clearing the prepared statement cache. This is database specific.
-
1
def clear_cache!
-
# this should be overridden by concrete adapters
-
end
-
-
# Returns true if its required to reload the connection between requests for development mode.
-
# This is not the case for Ruby/MySQL and it's not necessary for any adapters except SQLite.
-
1
def requires_reloading?
-
false
-
end
-
-
# Checks whether the connection to the database is still active (i.e. not stale).
-
# This is done under the hood by calling <tt>active?</tt>. If the connection
-
# is no longer active, then this method will reconnect to the database.
-
1
def verify!(*ignored)
-
1
reconnect! unless active?
-
end
-
-
# Provides access to the underlying database driver for this adapter. For
-
# example, this method returns a Mysql object in case of MysqlAdapter,
-
# and a PGconn object in case of PostgreSQLAdapter.
-
#
-
# This is useful for when you need to call a proprietary method such as
-
# PostgreSQL's lo_* methods.
-
1
def raw_connection
-
@connection
-
end
-
-
1
def open_transactions
-
@open_transactions ||= 0
-
end
-
-
1
def increment_open_transactions
-
@open_transactions ||= 0
-
@open_transactions += 1
-
end
-
-
1
def decrement_open_transactions
-
@open_transactions -= 1
-
end
-
-
1
def transaction_joinable=(joinable)
-
@transaction_joinable = joinable
-
end
-
-
1
def create_savepoint
-
end
-
-
1
def rollback_to_savepoint
-
end
-
-
1
def release_savepoint
-
end
-
-
1
def case_sensitive_modifier(node)
-
node
-
end
-
-
1
def current_savepoint_name
-
"active_record_#{open_transactions}"
-
end
-
-
1
protected
-
-
1
def log(sql, name = "SQL", binds = [])
-
9
@instrumenter.instrument(
-
"sql.active_record",
-
:sql => sql,
-
:name => name,
-
:connection_id => object_id,
-
9
:binds => binds) { yield }
-
rescue Exception => e
-
message = "#{e.class.name}: #{e.message}: #{sql}"
-
@logger.debug message if @logger
-
exception = translate_exception(e, message)
-
exception.set_backtrace e.backtrace
-
raise exception
-
end
-
-
1
def translate_exception(e, message)
-
# override in derived class
-
ActiveRecord::StatementInvalid.new(message)
-
end
-
-
end
-
end
-
end
-
1
require 'set'
-
-
1
module ActiveRecord
-
# :stopdoc:
-
1
module ConnectionAdapters
-
# An abstract definition of a column in a table.
-
1
class Column
-
1
TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].to_set
-
1
FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE'].to_set
-
-
1
module Format
-
1
ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/
-
1
ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
-
end
-
-
1
attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale
-
1
attr_accessor :primary, :coder
-
-
1
alias :encoded? :coder
-
-
# Instantiates a new column in the table.
-
#
-
# +name+ is the column's name, such as <tt>supplier_id</tt> in <tt>supplier_id int(11)</tt>.
-
# +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
-
# +sql_type+ is used to extract the column's length, if necessary. For example +60+ in
-
# <tt>company_name varchar(60)</tt>.
-
# It will be mapped to one of the standard Rails SQL types in the <tt>type</tt> attribute.
-
# +null+ determines if this column allows +NULL+ values.
-
1
def initialize(name, default, sql_type = nil, null = true)
-
14
@name = name
-
14
@sql_type = sql_type
-
14
@null = null
-
14
@limit = extract_limit(sql_type)
-
14
@precision = extract_precision(sql_type)
-
14
@scale = extract_scale(sql_type)
-
14
@type = simplified_type(sql_type)
-
14
@default = extract_default(default)
-
14
@primary = nil
-
14
@coder = nil
-
end
-
-
# Returns +true+ if the column is either of type string or text.
-
1
def text?
-
type == :string || type == :text
-
end
-
-
# Returns +true+ if the column is either of type integer, float or decimal.
-
1
def number?
-
18
type == :integer || type == :float || type == :decimal
-
end
-
-
1
def has_default?
-
!default.nil?
-
end
-
-
# Returns the Ruby class that corresponds to the abstract data type.
-
1
def klass
-
case type
-
when :integer then Fixnum
-
when :float then Float
-
when :decimal then BigDecimal
-
when :datetime, :timestamp, :time then Time
-
when :date then Date
-
when :text, :string, :binary then String
-
when :boolean then Object
-
end
-
end
-
-
# Casts value (which is a String) to an appropriate instance.
-
1
def type_cast(value)
-
23
return nil if value.nil?
-
9
return coder.load(value) if encoded?
-
-
9
klass = self.class
-
-
9
case type
-
9
when :string, :text then value
-
when :integer then value.to_i rescue value ? 1 : 0
-
when :float then value.to_f
-
when :decimal then klass.value_to_decimal(value)
-
when :datetime, :timestamp then klass.string_to_time(value)
-
when :time then klass.string_to_dummy_time(value)
-
when :date then klass.string_to_date(value)
-
when :binary then klass.binary_to_string(value)
-
when :boolean then klass.value_to_boolean(value)
-
else value
-
end
-
end
-
-
1
def type_cast_code(var_name)
-
5
klass = self.class.name
-
-
5
case type
-
4
when :string, :text then var_name
-
1
when :integer then "(#{var_name}.to_i rescue #{var_name} ? 1 : 0)"
-
when :float then "#{var_name}.to_f"
-
when :decimal then "#{klass}.value_to_decimal(#{var_name})"
-
when :datetime, :timestamp then "#{klass}.string_to_time(#{var_name})"
-
when :time then "#{klass}.string_to_dummy_time(#{var_name})"
-
when :date then "#{klass}.string_to_date(#{var_name})"
-
when :binary then "#{klass}.binary_to_string(#{var_name})"
-
when :boolean then "#{klass}.value_to_boolean(#{var_name})"
-
else var_name
-
end
-
end
-
-
# Returns the human name of the column name.
-
#
-
# ===== Examples
-
# Column.new('sales_stage', ...).human_name # => 'Sales stage'
-
1
def human_name
-
Base.human_attribute_name(@name)
-
end
-
-
1
def extract_default(default)
-
14
type_cast(default)
-
end
-
-
# Used to convert from Strings to BLOBs
-
1
def string_to_binary(value)
-
self.class.string_to_binary(value)
-
end
-
-
1
class << self
-
# Used to convert from Strings to BLOBs
-
1
def string_to_binary(value)
-
value
-
end
-
-
# Used to convert from BLOBs to Strings
-
1
def binary_to_string(value)
-
value
-
end
-
-
1
def string_to_date(string)
-
return string unless string.is_a?(String)
-
return nil if string.empty?
-
-
fast_string_to_date(string) || fallback_string_to_date(string)
-
end
-
-
1
def string_to_time(string)
-
return string unless string.is_a?(String)
-
return nil if string.empty?
-
-
fast_string_to_time(string) || fallback_string_to_time(string)
-
end
-
-
1
def string_to_dummy_time(string)
-
return string unless string.is_a?(String)
-
return nil if string.empty?
-
-
string_to_time "2000-01-01 #{string}"
-
end
-
-
# convert something to a boolean
-
1
def value_to_boolean(value)
-
if value.is_a?(String) && value.blank?
-
nil
-
else
-
TRUE_VALUES.include?(value)
-
end
-
end
-
-
# convert something to a BigDecimal
-
1
def value_to_decimal(value)
-
# Using .class is faster than .is_a? and
-
# subclasses of BigDecimal will be handled
-
# in the else clause
-
if value.class == BigDecimal
-
value
-
elsif value.respond_to?(:to_d)
-
value.to_d
-
else
-
value.to_s.to_d
-
end
-
end
-
-
1
protected
-
# '0.123456' -> 123456
-
# '1.123456' -> 123456
-
1
def microseconds(time)
-
((time[:sec_fraction].to_f % 1) * 1_000_000).to_i
-
end
-
-
1
def new_date(year, mon, mday)
-
if year && year != 0
-
Date.new(year, mon, mday) rescue nil
-
end
-
end
-
-
1
def new_time(year, mon, mday, hour, min, sec, microsec)
-
# Treat 0000-00-00 00:00:00 as nil.
-
return nil if year.nil? || (year == 0 && mon == 0 && mday == 0)
-
-
Time.time_with_datetime_fallback(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
-
end
-
-
1
def fast_string_to_date(string)
-
if string =~ Format::ISO_DATE
-
new_date $1.to_i, $2.to_i, $3.to_i
-
end
-
end
-
-
# Doesn't handle time zones.
-
1
def fast_string_to_time(string)
-
if string =~ Format::ISO_DATETIME
-
microsec = ($7.to_f * 1_000_000).to_i
-
new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
-
end
-
end
-
-
1
def fallback_string_to_date(string)
-
new_date(*::Date._parse(string, false).values_at(:year, :mon, :mday))
-
end
-
-
1
def fallback_string_to_time(string)
-
time_hash = Date._parse(string)
-
time_hash[:sec_fraction] = microseconds(time_hash)
-
-
new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction))
-
end
-
end
-
-
1
private
-
1
def extract_limit(sql_type)
-
14
$1.to_i if sql_type =~ /\((.*)\)/
-
end
-
-
1
def extract_precision(sql_type)
-
14
$2.to_i if sql_type =~ /^(numeric|decimal|number)\((\d+)(,\d+)?\)/i
-
end
-
-
1
def extract_scale(sql_type)
-
14
case sql_type
-
when /^(numeric|decimal|number)\((\d+)\)/i then 0
-
when /^(numeric|decimal|number)\((\d+)(,(\d+))\)/i then $4.to_i
-
end
-
end
-
-
1
def simplified_type(field_type)
-
14
case field_type
-
when /int/i
-
3
:integer
-
when /float|double/i
-
:float
-
when /decimal|numeric|number/i
-
extract_scale(field_type) == 0 ? :integer : :decimal
-
when /datetime/i
-
4
:datetime
-
when /timestamp/i
-
:timestamp
-
when /time/i
-
:time
-
when /date/i
-
:date
-
when /clob/i, /text/i
-
:text
-
when /blob/i, /binary/i
-
:binary
-
when /char/i, /string/i
-
7
:string
-
when /boolean/i
-
:boolean
-
end
-
end
-
end
-
end
-
# :startdoc:
-
end
-
1
require 'active_record/connection_adapters/sqlite_adapter'
-
-
1
gem 'sqlite3', '~> 1.3.4'
-
1
require 'sqlite3'
-
-
1
module ActiveRecord
-
1
class Base
-
# sqlite3 adapter reuses sqlite_connection.
-
1
def self.sqlite3_connection(config) # :nodoc:
-
# Require database.
-
1
unless config[:database]
-
raise ArgumentError, "No database file specified. Missing argument: database"
-
end
-
-
# Allow database path relative to Rails.root, but only if
-
# the database path is not the special path that tells
-
# Sqlite to build a database only in memory.
-
1
if defined?(Rails.root) && ':memory:' != config[:database]
-
1
config[:database] = File.expand_path(config[:database], Rails.root)
-
end
-
-
1
unless 'sqlite3' == config[:adapter]
-
raise ArgumentError, 'adapter name should be "sqlite3"'
-
end
-
-
1
db = SQLite3::Database.new(
-
config[:database],
-
:results_as_hash => true
-
)
-
-
1
db.busy_timeout(config[:timeout]) if config[:timeout]
-
-
1
ConnectionAdapters::SQLite3Adapter.new(db, logger, config)
-
end
-
end
-
-
1
module ConnectionAdapters #:nodoc:
-
1
class SQLite3Adapter < SQLiteAdapter # :nodoc:
-
1
def quote(value, column = nil)
-
if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
-
s = column.class.string_to_binary(value).unpack("H*")[0]
-
"x'#{s}'"
-
else
-
super
-
end
-
end
-
-
# Returns the current database encoding format as a string, eg: 'UTF-8'
-
1
def encoding
-
if @connection.respond_to?(:encoding)
-
@connection.encoding.to_s
-
else
-
@connection.execute('PRAGMA encoding')[0]['encoding']
-
end
-
end
-
-
end
-
end
-
end
-
1
require 'active_record/connection_adapters/abstract_adapter'
-
1
require 'active_support/core_ext/kernel/requires'
-
-
1
module ActiveRecord
-
1
module ConnectionAdapters #:nodoc:
-
1
class SQLiteColumn < Column #:nodoc:
-
1
class << self
-
1
def string_to_binary(value)
-
value.gsub(/\0|\%/n) do |b|
-
case b
-
when "\0" then "%00"
-
when "%" then "%25"
-
end
-
end
-
end
-
-
1
def binary_to_string(value)
-
if value.respond_to?(:force_encoding) && value.encoding != Encoding::ASCII_8BIT
-
value = value.force_encoding(Encoding::ASCII_8BIT)
-
end
-
-
value.gsub(/%00|%25/n) do |b|
-
case b
-
when "%00" then "\0"
-
when "%25" then "%"
-
end
-
end
-
end
-
end
-
end
-
-
# The SQLite adapter works with both the 2.x and 3.x series of SQLite with the sqlite-ruby
-
# drivers (available both as gems and from http://rubyforge.org/projects/sqlite-ruby/).
-
#
-
# Options:
-
#
-
# * <tt>:database</tt> - Path to the database file.
-
1
class SQLiteAdapter < AbstractAdapter
-
1
class Version
-
1
include Comparable
-
-
1
def initialize(version_string)
-
@version = version_string.split('.').map { |v| v.to_i }
-
end
-
-
1
def <=>(version_string)
-
@version <=> version_string.split('.').map { |v| v.to_i }
-
end
-
end
-
-
1
def initialize(connection, logger, config)
-
1
super(connection, logger)
-
1
@statements = {}
-
1
@config = config
-
end
-
-
1
def self.visitor_for(pool) # :nodoc:
-
1
Arel::Visitors::SQLite.new(pool)
-
end
-
-
1
def adapter_name #:nodoc:
-
'SQLite'
-
end
-
-
# Returns true if SQLite version is '2.0.0' or greater, false otherwise.
-
1
def supports_ddl_transactions?
-
sqlite_version >= '2.0.0'
-
end
-
-
# Returns true if SQLite version is '3.6.8' or greater, false otherwise.
-
1
def supports_savepoints?
-
sqlite_version >= '3.6.8'
-
end
-
-
# Returns true, since this connection adapter supports prepared statement
-
# caching.
-
1
def supports_statement_cache?
-
true
-
end
-
-
# Returns true, since this connection adapter supports migrations.
-
1
def supports_migrations? #:nodoc:
-
true
-
end
-
-
# Returns true.
-
1
def supports_primary_key? #:nodoc:
-
true
-
end
-
-
1
def requires_reloading?
-
true
-
end
-
-
# Returns true if SQLite version is '3.1.6' or greater, false otherwise.
-
1
def supports_add_column?
-
sqlite_version >= '3.1.6'
-
end
-
-
# Disconnects from the database if already connected. Otherwise, this
-
# method does nothing.
-
1
def disconnect!
-
super
-
clear_cache!
-
@connection.close rescue nil
-
end
-
-
# Clears the prepared statements cache.
-
1
def clear_cache!
-
@statements.values.each { |hash| hash[:stmt].close }
-
@statements.clear
-
end
-
-
# Returns true if SQLite version is '3.2.6' or greater, false otherwise.
-
1
def supports_count_distinct? #:nodoc:
-
sqlite_version >= '3.2.6'
-
end
-
-
# Returns true if SQLite version is '3.1.0' or greater, false otherwise.
-
1
def supports_autoincrement? #:nodoc:
-
sqlite_version >= '3.1.0'
-
end
-
-
1
def native_database_types #:nodoc:
-
{
-
:primary_key => default_primary_key_type,
-
:string => { :name => "varchar", :limit => 255 },
-
:text => { :name => "text" },
-
:integer => { :name => "integer" },
-
:float => { :name => "float" },
-
:decimal => { :name => "decimal" },
-
:datetime => { :name => "datetime" },
-
:timestamp => { :name => "datetime" },
-
:time => { :name => "time" },
-
:date => { :name => "date" },
-
:binary => { :name => "blob" },
-
:boolean => { :name => "boolean" }
-
}
-
end
-
-
-
# QUOTING ==================================================
-
-
1
def quote_string(s) #:nodoc:
-
@connection.class.quote(s)
-
end
-
-
1
def quote_column_name(name) #:nodoc:
-
5
%Q("#{name.to_s.gsub('"', '""')}")
-
end
-
-
# Quote date/time values for use in SQL input. Includes microseconds
-
# if the value is a Time responding to usec.
-
1
def quoted_date(value) #:nodoc:
-
if value.respond_to?(:usec)
-
"#{super}.#{sprintf("%06d", value.usec)}"
-
else
-
super
-
end
-
end
-
-
1
def type_cast(value, column) # :nodoc:
-
return super unless BigDecimal === value
-
-
value.to_f
-
end
-
-
# DATABASE STATEMENTS ======================================
-
-
1
def exec_query(sql, name = nil, binds = [])
-
9
log(sql, name, binds) do
-
-
# Don't cache statements without bind values
-
9
if binds.empty?
-
9
stmt = @connection.prepare(sql)
-
9
cols = stmt.columns
-
9
records = stmt.to_a
-
9
stmt.close
-
9
stmt = records
-
else
-
cache = @statements[sql] ||= {
-
:stmt => @connection.prepare(sql)
-
}
-
stmt = cache[:stmt]
-
cols = cache[:cols] ||= stmt.columns
-
stmt.reset!
-
stmt.bind_params binds.map { |col, val|
-
type_cast(val, col)
-
}
-
end
-
-
9
ActiveRecord::Result.new(cols, stmt.to_a)
-
end
-
end
-
-
1
def exec_delete(sql, name = 'SQL', binds = [])
-
exec_query(sql, name, binds)
-
@connection.changes
-
end
-
1
alias :exec_update :exec_delete
-
-
1
def last_inserted_id(result)
-
@connection.last_insert_row_id
-
end
-
-
1
def execute(sql, name = nil) #:nodoc:
-
log(sql, name) { @connection.execute(sql) }
-
end
-
-
1
def update_sql(sql, name = nil) #:nodoc:
-
super
-
@connection.changes
-
end
-
-
1
def delete_sql(sql, name = nil) #:nodoc:
-
sql += " WHERE 1=1" unless sql =~ /WHERE/i
-
super sql, name
-
end
-
-
1
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
-
super
-
id_value || @connection.last_insert_row_id
-
end
-
1
alias :create :insert_sql
-
-
1
def select_rows(sql, name = nil)
-
exec_query(sql, name).rows
-
end
-
-
1
def create_savepoint
-
execute("SAVEPOINT #{current_savepoint_name}")
-
end
-
-
1
def rollback_to_savepoint
-
execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
-
end
-
-
1
def release_savepoint
-
execute("RELEASE SAVEPOINT #{current_savepoint_name}")
-
end
-
-
1
def begin_db_transaction #:nodoc:
-
@connection.transaction
-
end
-
-
1
def commit_db_transaction #:nodoc:
-
@connection.commit
-
end
-
-
1
def rollback_db_transaction #:nodoc:
-
@connection.rollback
-
end
-
-
# SCHEMA STATEMENTS ========================================
-
-
1
def tables(name = 'SCHEMA') #:nodoc:
-
4
sql = <<-SQL
-
SELECT name
-
FROM sqlite_master
-
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
-
SQL
-
-
4
exec_query(sql, name).map do |row|
-
16
row['name']
-
end
-
end
-
-
# Returns an array of +SQLiteColumn+ objects for the table specified by +table_name+.
-
1
def columns(table_name, name = nil) #:nodoc:
-
2
table_structure(table_name).map do |field|
-
14
case field["dflt_value"]
-
when /^null$/i
-
field["dflt_value"] = nil
-
when /^'(.*)'$/
-
field["dflt_value"] = $1.gsub(/''/, "'")
-
when /^"(.*)"$/
-
field["dflt_value"] = $1.gsub(/""/, '"')
-
end
-
-
14
SQLiteColumn.new(field['name'], field['dflt_value'], field['type'], field['notnull'].to_i == 0)
-
end
-
end
-
-
# Returns an array of indexes for the given table.
-
1
def indexes(table_name, name = nil) #:nodoc:
-
exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", name).map do |row|
-
IndexDefinition.new(
-
table_name,
-
row['name'],
-
row['unique'] != 0,
-
exec_query("PRAGMA index_info('#{row['name']}')").map { |col|
-
col['name']
-
})
-
end
-
end
-
-
1
def primary_key(table_name) #:nodoc:
-
3
column = table_structure(table_name).find { |field|
-
3
field['pk'] == 1
-
}
-
3
column && column['name']
-
end
-
-
1
def remove_index!(table_name, index_name) #:nodoc:
-
exec_query "DROP INDEX #{quote_column_name(index_name)}"
-
end
-
-
# Renames a table.
-
#
-
# Example:
-
# rename_table('octopuses', 'octopi')
-
1
def rename_table(name, new_name)
-
exec_query "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
-
end
-
-
# See: http://www.sqlite.org/lang_altertable.html
-
# SQLite has an additional restriction on the ALTER TABLE statement
-
1
def valid_alter_table_options( type, options)
-
type.to_sym != :primary_key
-
end
-
-
1
def add_column(table_name, column_name, type, options = {}) #:nodoc:
-
if supports_add_column? && valid_alter_table_options( type, options )
-
super(table_name, column_name, type, options)
-
else
-
alter_table(table_name) do |definition|
-
definition.column(column_name, type, options)
-
end
-
end
-
end
-
-
1
def remove_column(table_name, *column_names) #:nodoc:
-
raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
-
column_names.flatten.each do |column_name|
-
alter_table(table_name) do |definition|
-
definition.columns.delete(definition[column_name])
-
end
-
end
-
end
-
1
alias :remove_columns :remove_column
-
-
1
def change_column_default(table_name, column_name, default) #:nodoc:
-
alter_table(table_name) do |definition|
-
definition[column_name].default = default
-
end
-
end
-
-
1
def change_column_null(table_name, column_name, null, default = nil)
-
unless null || default.nil?
-
exec_query("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
-
end
-
alter_table(table_name) do |definition|
-
definition[column_name].null = null
-
end
-
end
-
-
1
def change_column(table_name, column_name, type, options = {}) #:nodoc:
-
alter_table(table_name) do |definition|
-
include_default = options_include_default?(options)
-
definition[column_name].instance_eval do
-
self.type = type
-
self.limit = options[:limit] if options.include?(:limit)
-
self.default = options[:default] if include_default
-
self.null = options[:null] if options.include?(:null)
-
end
-
end
-
end
-
-
1
def rename_column(table_name, column_name, new_column_name) #:nodoc:
-
unless columns(table_name).detect{|c| c.name == column_name.to_s }
-
raise ActiveRecord::ActiveRecordError, "Missing column #{table_name}.#{column_name}"
-
end
-
alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s})
-
end
-
-
1
def empty_insert_statement_value
-
"VALUES(NULL)"
-
end
-
-
1
protected
-
1
def select(sql, name = nil, binds = []) #:nodoc:
-
exec_query(sql, name, binds).to_a
-
end
-
-
1
def table_structure(table_name)
-
5
structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA').to_hash
-
5
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
-
5
structure
-
end
-
-
1
def alter_table(table_name, options = {}) #:nodoc:
-
altered_table_name = "altered_#{table_name}"
-
caller = lambda {|definition| yield definition if block_given?}
-
-
transaction do
-
move_table(table_name, altered_table_name,
-
options.merge(:temporary => true))
-
move_table(altered_table_name, table_name, &caller)
-
end
-
end
-
-
1
def move_table(from, to, options = {}, &block) #:nodoc:
-
copy_table(from, to, options, &block)
-
drop_table(from)
-
end
-
-
1
def copy_table(from, to, options = {}) #:nodoc:
-
options = options.merge(:id => (!columns(from).detect{|c| c.name == 'id'}.nil? && 'id' == primary_key(from).to_s))
-
create_table(to, options) do |definition|
-
@definition = definition
-
columns(from).each do |column|
-
column_name = options[:rename] ?
-
(options[:rename][column.name] ||
-
options[:rename][column.name.to_sym] ||
-
column.name) : column.name
-
-
@definition.column(column_name, column.type,
-
:limit => column.limit, :default => column.default,
-
:null => column.null)
-
end
-
@definition.primary_key(primary_key(from)) if primary_key(from)
-
yield @definition if block_given?
-
end
-
-
copy_table_indexes(from, to, options[:rename] || {})
-
copy_table_contents(from, to,
-
@definition.columns.map {|column| column.name},
-
options[:rename] || {})
-
end
-
-
1
def copy_table_indexes(from, to, rename = {}) #:nodoc:
-
indexes(from).each do |index|
-
name = index.name
-
if to == "altered_#{from}"
-
name = "temp_#{name}"
-
elsif from == "altered_#{to}"
-
name = name[5..-1]
-
end
-
-
to_column_names = columns(to).map { |c| c.name }
-
columns = index.columns.map {|c| rename[c] || c }.select do |column|
-
to_column_names.include?(column)
-
end
-
-
unless columns.empty?
-
# index name can't be the same
-
opts = { :name => name.gsub(/_(#{from})_/, "_#{to}_") }
-
opts[:unique] = true if index.unique
-
add_index(to, columns, opts)
-
end
-
end
-
end
-
-
1
def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
-
column_mappings = Hash[columns.map {|name| [name, name]}]
-
rename.each { |a| column_mappings[a.last] = a.first }
-
from_columns = columns(from).collect {|col| col.name}
-
columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
-
quoted_columns = columns.map { |col| quote_column_name(col) } * ','
-
-
quoted_to = quote_table_name(to)
-
exec_query("SELECT * FROM #{quote_table_name(from)}").each do |row|
-
sql = "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES ("
-
sql << columns.map {|col| quote row[column_mappings[col]]} * ', '
-
sql << ')'
-
exec_query sql
-
end
-
end
-
-
1
def sqlite_version
-
@sqlite_version ||= SQLiteAdapter::Version.new(select_value('select sqlite_version(*)'))
-
end
-
-
1
def default_primary_key_type
-
if supports_autoincrement?
-
'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL'
-
else
-
'INTEGER PRIMARY KEY NOT NULL'
-
end
-
end
-
-
1
def translate_exception(exception, message)
-
case exception.message
-
when /column(s)? .* (is|are) not unique/
-
RecordNotUnique.new(message, exception)
-
else
-
super
-
end
-
end
-
-
end
-
end
-
end
-
1
module ActiveRecord
-
# = Active Record Counter Cache
-
1
module CounterCache
-
# Resets one or more counter caches to their correct value using an SQL
-
# count query. This is useful when adding new counter caches, or if the
-
# counter has been corrupted or modified directly by SQL.
-
#
-
# ==== Parameters
-
#
-
# * +id+ - The id of the object you wish to reset a counter on.
-
# * +counters+ - One or more counter names to reset
-
#
-
# ==== Examples
-
#
-
# # For Post with id #1 records reset the comments_count
-
# Post.reset_counters(1, :comments)
-
1
def reset_counters(id, *counters)
-
object = find(id)
-
counters.each do |association|
-
has_many_association = reflect_on_association(association.to_sym)
-
-
expected_name = if has_many_association.options[:as]
-
has_many_association.options[:as].to_s.classify
-
else
-
self.name
-
end
-
-
child_class = has_many_association.klass
-
belongs_to = child_class.reflect_on_all_associations(:belongs_to)
-
reflection = belongs_to.find { |e| e.class_name == expected_name }
-
counter_name = reflection.counter_cache_column
-
-
stmt = unscoped.where(arel_table[primary_key].eq(object.id)).arel.compile_update({
-
arel_table[counter_name] => object.send(association).count
-
})
-
connection.update stmt
-
end
-
return true
-
end
-
-
# A generic "counter updater" implementation, intended primarily to be
-
# used by increment_counter and decrement_counter, but which may also
-
# be useful on its own. It simply does a direct SQL update for the record
-
# with the given ID, altering the given hash of counters by the amount
-
# given by the corresponding value:
-
#
-
# ==== Parameters
-
#
-
# * +id+ - The id of the object you wish to update a counter on or an Array of ids.
-
# * +counters+ - An Array of Hashes containing the names of the fields
-
# to update as keys and the amount to update the field by as values.
-
#
-
# ==== Examples
-
#
-
# # For the Post with id of 5, decrement the comment_count by 1, and
-
# # increment the action_count by 1
-
# Post.update_counters 5, :comment_count => -1, :action_count => 1
-
# # Executes the following SQL:
-
# # UPDATE posts
-
# # SET comment_count = COALESCE(comment_count, 0) - 1,
-
# # action_count = COALESCE(action_count, 0) + 1
-
# # WHERE id = 5
-
#
-
# # For the Posts with id of 10 and 15, increment the comment_count by 1
-
# Post.update_counters [10, 15], :comment_count => 1
-
# # Executes the following SQL:
-
# # UPDATE posts
-
# # SET comment_count = COALESCE(comment_count, 0) + 1,
-
# # WHERE id IN (10, 15)
-
1
def update_counters(id, counters)
-
updates = counters.map do |counter_name, value|
-
operator = value < 0 ? '-' : '+'
-
quoted_column = connection.quote_column_name(counter_name)
-
"#{quoted_column} = COALESCE(#{quoted_column}, 0) #{operator} #{value.abs}"
-
end
-
-
IdentityMap.remove_by_id(symbolized_base_class, id) if IdentityMap.enabled?
-
-
update_all(updates.join(', '), primary_key => id )
-
end
-
-
# Increment a number field by one, usually representing a count.
-
#
-
# This is used for caching aggregate values, so that they don't need to be computed every time.
-
# For example, a DiscussionBoard may cache post_count and comment_count otherwise every time the board is
-
# shown it would have to run an SQL query to find how many posts and comments there are.
-
#
-
# ==== Parameters
-
#
-
# * +counter_name+ - The name of the field that should be incremented.
-
# * +id+ - The id of the object that should be incremented.
-
#
-
# ==== Examples
-
#
-
# # Increment the post_count column for the record with an id of 5
-
# DiscussionBoard.increment_counter(:post_count, 5)
-
1
def increment_counter(counter_name, id)
-
update_counters(id, counter_name => 1)
-
end
-
-
# Decrement a number field by one, usually representing a count.
-
#
-
# This works the same as increment_counter but reduces the column value by 1 instead of increasing it.
-
#
-
# ==== Parameters
-
#
-
# * +counter_name+ - The name of the field that should be decremented.
-
# * +id+ - The id of the object that should be decremented.
-
#
-
# ==== Examples
-
#
-
# # Decrement the post_count column for the record with an id of 5
-
# DiscussionBoard.decrement_counter(:post_count, 5)
-
1
def decrement_counter(counter_name, id)
-
update_counters(id, counter_name => -1)
-
end
-
end
-
end
-
1
module ActiveRecord
-
-
# = Active Record Dynamic Finder Match
-
#
-
# Refer to ActiveRecord::Base documentation for Dynamic attribute-based finders for detailed info
-
#
-
1
class DynamicFinderMatch
-
1
def self.match(method)
-
76
finder = :first
-
76
bang = false
-
76
instantiator = nil
-
-
76
case method.to_s
-
when /^find_(all_|last_)?by_([_a-zA-Z]\w*)$/
-
finder = :last if $1 == 'last_'
-
finder = :all if $1 == 'all_'
-
names = $2
-
when /^find_by_([_a-zA-Z]\w*)\!$/
-
bang = true
-
names = $1
-
when /^find_or_(initialize|create)_by_([_a-zA-Z]\w*)$/
-
instantiator = $1 == 'initialize' ? :new : :create
-
names = $2
-
else
-
76
return nil
-
end
-
-
new(finder, instantiator, bang, names.split('_and_'))
-
end
-
-
1
def initialize(finder, instantiator, bang, attribute_names)
-
@finder = finder
-
@instantiator = instantiator
-
@bang = bang
-
@attribute_names = attribute_names
-
end
-
-
1
attr_reader :finder, :attribute_names, :instantiator
-
-
1
def finder?
-
@finder && !@instantiator
-
end
-
-
1
def instantiator?
-
@finder == :first && @instantiator
-
end
-
-
1
def creator?
-
@finder == :first && @instantiator == :create
-
end
-
-
1
def bang?
-
@bang
-
end
-
end
-
end
-
1
module ActiveRecord
-
-
# = Active Record Dynamic Scope Match
-
#
-
# Provides dynamic attribute-based scopes such as <tt>scoped_by_price(4.99)</tt>
-
# if, for example, the <tt>Product</tt> has an attribute with that name. You can
-
# chain more <tt>scoped_by_* </tt> methods after the other. It acts like a named
-
# scope except that it's dynamic.
-
1
class DynamicScopeMatch
-
1
def self.match(method)
-
76
return unless method.to_s =~ /^scoped_by_([_a-zA-Z]\w*)$/
-
new(true, $1 && $1.split('_and_'))
-
end
-
-
1
def initialize(scope, attribute_names)
-
@scope = scope
-
@attribute_names = attribute_names
-
end
-
-
1
attr_reader :scope, :attribute_names
-
1
alias :scope? :scope
-
end
-
end
-
1
module ActiveRecord
-
-
# = Active Record Errors
-
#
-
# Generic Active Record exception class.
-
1
class ActiveRecordError < StandardError
-
end
-
-
# Raised when the single-table inheritance mechanism fails to locate the subclass
-
# (for example due to improper usage of column that +inheritance_column+ points to).
-
1
class SubclassNotFound < ActiveRecordError #:nodoc:
-
end
-
-
# Raised when an object assigned to an association has an incorrect type.
-
#
-
# class Ticket < ActiveRecord::Base
-
# has_many :patches
-
# end
-
#
-
# class Patch < ActiveRecord::Base
-
# belongs_to :ticket
-
# end
-
#
-
# # Comments are not patches, this assignment raises AssociationTypeMismatch.
-
# @ticket.patches << Comment.new(:content => "Please attach tests to your patch.")
-
1
class AssociationTypeMismatch < ActiveRecordError
-
end
-
-
# Raised when unserialized object's type mismatches one specified for serializable field.
-
1
class SerializationTypeMismatch < ActiveRecordError
-
end
-
-
# Raised when adapter not specified on connection (or configuration file <tt>config/database.yml</tt>
-
# misses adapter field).
-
1
class AdapterNotSpecified < ActiveRecordError
-
end
-
-
# Raised when Active Record cannot find database adapter specified in <tt>config/database.yml</tt> or programmatically.
-
1
class AdapterNotFound < ActiveRecordError
-
end
-
-
# Raised when connection to the database could not been established (for example when <tt>connection=</tt>
-
# is given a nil object).
-
1
class ConnectionNotEstablished < ActiveRecordError
-
end
-
-
# Raised when Active Record cannot find record by given id or set of ids.
-
1
class RecordNotFound < ActiveRecordError
-
end
-
-
# Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
-
# saved because record is invalid.
-
1
class RecordNotSaved < ActiveRecordError
-
end
-
-
# Raised when SQL statement cannot be executed by the database (for example, it's often the case for
-
# MySQL when Ruby driver used is too old).
-
1
class StatementInvalid < ActiveRecordError
-
end
-
-
# Raised when SQL statement is invalid and the application gets a blank result.
-
1
class ThrowResult < ActiveRecordError
-
end
-
-
# Parent class for all specific exceptions which wrap database driver exceptions
-
# provides access to the original exception also.
-
1
class WrappedDatabaseException < StatementInvalid
-
1
attr_reader :original_exception
-
-
1
def initialize(message, original_exception)
-
super(message)
-
@original_exception = original_exception
-
end
-
end
-
-
# Raised when a record cannot be inserted because it would violate a uniqueness constraint.
-
1
class RecordNotUnique < WrappedDatabaseException
-
end
-
-
# Raised when a record cannot be inserted or updated because it references a non-existent record.
-
1
class InvalidForeignKey < WrappedDatabaseException
-
end
-
-
# Raised when number of bind variables in statement given to <tt>:condition</tt> key (for example,
-
# when using +find+ method)
-
# does not match number of expected variables.
-
#
-
# For example, in
-
#
-
# Location.find :all, :conditions => ["lat = ? AND lng = ?", 53.7362]
-
#
-
# two placeholders are given but only one variable to fill them.
-
1
class PreparedStatementInvalid < ActiveRecordError
-
end
-
-
# Raised on attempt to save stale record. Record is stale when it's being saved in another query after
-
# instantiation, for example, when two users edit the same wiki page and one starts editing and saves
-
# the page before the other.
-
#
-
# Read more about optimistic locking in ActiveRecord::Locking module RDoc.
-
1
class StaleObjectError < ActiveRecordError
-
end
-
-
# Raised when association is being configured improperly or
-
# user tries to use offset and limit together with has_many or has_and_belongs_to_many associations.
-
1
class ConfigurationError < ActiveRecordError
-
end
-
-
# Raised on attempt to update record that is instantiated as read only.
-
1
class ReadOnlyRecord < ActiveRecordError
-
end
-
-
# ActiveRecord::Transactions::ClassMethods.transaction uses this exception
-
# to distinguish a deliberate rollback from other exceptional situations.
-
# Normally, raising an exception will cause the +transaction+ method to rollback
-
# the database transaction *and* pass on the exception. But if you raise an
-
# ActiveRecord::Rollback exception, then the database transaction will be rolled back,
-
# without passing on the exception.
-
#
-
# For example, you could do this in your controller to rollback a transaction:
-
#
-
# class BooksController < ActionController::Base
-
# def create
-
# Book.transaction do
-
# book = Book.new(params[:book])
-
# book.save!
-
# if today_is_friday?
-
# # The system must fail on Friday so that our support department
-
# # won't be out of job. We silently rollback this transaction
-
# # without telling the user.
-
# raise ActiveRecord::Rollback, "Call tech support!"
-
# end
-
# end
-
# # ActiveRecord::Rollback is the only exception that won't be passed on
-
# # by ActiveRecord::Base.transaction, so this line will still be reached
-
# # even on Friday.
-
# redirect_to root_url
-
# end
-
# end
-
1
class Rollback < ActiveRecordError
-
end
-
-
# Raised when attribute has a name reserved by Active Record (when attribute has name of one of Active Record instance methods).
-
1
class DangerousAttributeError < ActiveRecordError
-
end
-
-
# Raised when unknown attributes are supplied via mass assignment.
-
1
class UnknownAttributeError < NoMethodError
-
end
-
-
# Raised when an error occurred while doing a mass assignment to an attribute through the
-
# <tt>attributes=</tt> method. The exception has an +attribute+ property that is the name of the
-
# offending attribute.
-
1
class AttributeAssignmentError < ActiveRecordError
-
1
attr_reader :exception, :attribute
-
1
def initialize(message, exception, attribute)
-
@exception = exception
-
@attribute = attribute
-
@message = message
-
end
-
end
-
-
# Raised when there are multiple errors while doing a mass assignment through the +attributes+
-
# method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
-
# objects, each corresponding to the error while assigning to an attribute.
-
1
class MultiparameterAssignmentErrors < ActiveRecordError
-
1
attr_reader :errors
-
1
def initialize(errors)
-
@errors = errors
-
end
-
end
-
end
-
1
require 'erb'
-
-
1
begin
-
1
require 'psych'
-
rescue LoadError
-
end
-
-
1
require 'yaml'
-
1
require 'csv'
-
1
require 'zlib'
-
1
require 'active_support/dependencies'
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/logger'
-
1
require 'active_support/ordered_hash'
-
1
require 'active_support/core_ext/module/deprecation'
-
-
1
if defined? ActiveRecord
-
1
class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
-
end
-
else
-
class FixtureClassNotFound < StandardError #:nodoc:
-
end
-
end
-
-
1
class FixturesFileNotFound < StandardError; end
-
-
# Fixtures are a way of organizing data that you want to test against; in short, sample data.
-
#
-
# = Fixture formats
-
#
-
# Fixtures come in 1 flavor:
-
#
-
# 1. YAML fixtures
-
#
-
# == YAML fixtures
-
#
-
# This type of fixture is in YAML format and the preferred default. YAML is a file format which describes data structures
-
# in a non-verbose, human-readable format. It ships with Ruby 1.8.1+.
-
#
-
# Unlike single-file fixtures, YAML fixtures are stored in a single file per model, which are placed
-
# in the directory appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is
-
# automatically configured for Rails, so you can just put your files in <tt><your-rails-app>/test/fixtures/</tt>).
-
# The fixture file ends with the <tt>.yml</tt> file extension (Rails example:
-
# <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>). The format of a YAML fixture file looks like this:
-
#
-
# rubyonrails:
-
# id: 1
-
# name: Ruby on Rails
-
# url: http://www.rubyonrails.org
-
#
-
# google:
-
# id: 2
-
# name: Google
-
# url: http://www.google.com
-
#
-
# This YAML fixture file includes two fixtures. Each YAML fixture (ie. record) is given a name and is followed by an
-
# indented list of key/value pairs in the "key: value" format. Records are separated by a blank line for your viewing
-
# pleasure.
-
#
-
# Note that YAML fixtures are unordered. If you want ordered fixtures, use the omap YAML type.
-
# See http://yaml.org/type/omap.html
-
# for the specification. You will need ordered fixtures when you have foreign key constraints on keys in the same table.
-
# This is commonly needed for tree structures. Example:
-
#
-
# --- !omap
-
# - parent:
-
# id: 1
-
# parent_id: NULL
-
# title: Parent
-
# - child:
-
# id: 2
-
# parent_id: 1
-
# title: Child
-
#
-
# = Using fixtures in testcases
-
#
-
# Since fixtures are a testing construct, we use them in our unit and functional tests. There are two ways to use the
-
# fixtures, but first let's take a look at a sample unit test:
-
#
-
# require 'test_helper'
-
#
-
# class WebSiteTest < ActiveSupport::TestCase
-
# test "web_site_count" do
-
# assert_equal 2, WebSite.count
-
# end
-
# end
-
#
-
# By default, the <tt>test_helper module</tt> will load all of your fixtures into your test database,
-
# so this test will succeed.
-
# The testing environment will automatically load the all fixtures into the database before each test.
-
# To ensure consistent data, the environment deletes the fixtures before running the load.
-
#
-
# In addition to being available in the database, the fixture's data may also be accessed by
-
# using a special dynamic method, which has the same name as the model, and accepts the
-
# name of the fixture to instantiate:
-
#
-
# test "find" do
-
# assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
-
# end
-
#
-
# Alternatively, you may enable auto-instantiation of the fixture data. For instance, take the following tests:
-
#
-
# test "find_alt_method_1" do
-
# assert_equal "Ruby on Rails", @web_sites['rubyonrails']['name']
-
# end
-
#
-
# test "find_alt_method_2" do
-
# assert_equal "Ruby on Rails", @rubyonrails.news
-
# end
-
#
-
# In order to use these methods to access fixtured data within your testcases, you must specify one of the
-
# following in your <tt>ActiveSupport::TestCase</tt>-derived class:
-
#
-
# - to fully enable instantiated fixtures (enable alternate methods #1 and #2 above)
-
# self.use_instantiated_fixtures = true
-
#
-
# - create only the hash for the fixtures, do not 'find' each instance (enable alternate method #1 only)
-
# self.use_instantiated_fixtures = :no_instances
-
#
-
# Using either of these alternate methods incurs a performance hit, as the fixtured data must be fully
-
# traversed in the database to create the fixture hash and/or instance variables. This is expensive for
-
# large sets of fixtured data.
-
#
-
# = Dynamic fixtures with ERB
-
#
-
# Some times you don't care about the content of the fixtures as much as you care about the volume. In these cases, you can
-
# mix ERB in with your YAML fixtures to create a bunch of fixtures for load testing, like:
-
#
-
# <% for i in 1..1000 %>
-
# fix_<%= i %>:
-
# id: <%= i %>
-
# name: guy_<%= 1 %>
-
# <% end %>
-
#
-
# This will create 1000 very simple YAML fixtures.
-
#
-
# Using ERB, you can also inject dynamic values into your fixtures with inserts like <tt><%= Date.today.strftime("%Y-%m-%d") %></tt>.
-
# This is however a feature to be used with some caution. The point of fixtures are that they're
-
# stable units of predictable sample data. If you feel that you need to inject dynamic values, then
-
# perhaps you should reexamine whether your application is properly testable. Hence, dynamic values
-
# in fixtures are to be considered a code smell.
-
#
-
# = Transactional fixtures
-
#
-
# TestCases can use begin+rollback to isolate their changes to the database instead of having to
-
# delete+insert for every test case.
-
#
-
# class FooTest < ActiveSupport::TestCase
-
# self.use_transactional_fixtures = true
-
#
-
# test "godzilla" do
-
# assert !Foo.find(:all).empty?
-
# Foo.destroy_all
-
# assert Foo.find(:all).empty?
-
# end
-
#
-
# test "godzilla aftermath" do
-
# assert !Foo.find(:all).empty?
-
# end
-
# end
-
#
-
# If you preload your test database with all fixture data (probably in the Rakefile task) and use transactional fixtures,
-
# then you may omit all fixtures declarations in your test cases since all the data's already there
-
# and every case rolls back its changes.
-
#
-
# In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to true. This will provide
-
# access to fixture data for every table that has been loaded through fixtures (depending on the
-
# value of +use_instantiated_fixtures+)
-
#
-
# When *not* to use transactional fixtures:
-
#
-
# 1. You're testing whether a transaction works correctly. Nested transactions don't commit until
-
# all parent transactions commit, particularly, the fixtures transaction which is begun in setup
-
# and rolled back in teardown. Thus, you won't be able to verify
-
# the results of your transaction until Active Record supports nested transactions or savepoints (in progress).
-
# 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
-
# Use InnoDB, MaxDB, or NDB instead.
-
#
-
# = Advanced YAML Fixtures
-
#
-
# YAML fixtures that don't specify an ID get some extra features:
-
#
-
# * Stable, autogenerated IDs
-
# * Label references for associations (belongs_to, has_one, has_many)
-
# * HABTM associations as inline lists
-
# * Autofilled timestamp columns
-
# * Fixture label interpolation
-
# * Support for YAML defaults
-
#
-
# == Stable, autogenerated IDs
-
#
-
# Here, have a monkey fixture:
-
#
-
# george:
-
# id: 1
-
# name: George the Monkey
-
#
-
# reginald:
-
# id: 2
-
# name: Reginald the Pirate
-
#
-
# Each of these fixtures has two unique identifiers: one for the database
-
# and one for the humans. Why don't we generate the primary key instead?
-
# Hashing each fixture's label yields a consistent ID:
-
#
-
# george: # generated id: 503576764
-
# name: George the Monkey
-
#
-
# reginald: # generated id: 324201669
-
# name: Reginald the Pirate
-
#
-
# Active Record looks at the fixture's model class, discovers the correct
-
# primary key, and generates it right before inserting the fixture
-
# into the database.
-
#
-
# The generated ID for a given label is constant, so we can discover
-
# any fixture's ID without loading anything, as long as we know the label.
-
#
-
# == Label references for associations (belongs_to, has_one, has_many)
-
#
-
# Specifying foreign keys in fixtures can be very fragile, not to
-
# mention difficult to read. Since Active Record can figure out the ID of
-
# any fixture from its label, you can specify FK's by label instead of ID.
-
#
-
# === belongs_to
-
#
-
# Let's break out some more monkeys and pirates.
-
#
-
# ### in pirates.yml
-
#
-
# reginald:
-
# id: 1
-
# name: Reginald the Pirate
-
# monkey_id: 1
-
#
-
# ### in monkeys.yml
-
#
-
# george:
-
# id: 1
-
# name: George the Monkey
-
# pirate_id: 1
-
#
-
# Add a few more monkeys and pirates and break this into multiple files,
-
# and it gets pretty hard to keep track of what's going on. Let's
-
# use labels instead of IDs:
-
#
-
# ### in pirates.yml
-
#
-
# reginald:
-
# name: Reginald the Pirate
-
# monkey: george
-
#
-
# ### in monkeys.yml
-
#
-
# george:
-
# name: George the Monkey
-
# pirate: reginald
-
#
-
# Pow! All is made clear. Active Record reflects on the fixture's model class,
-
# finds all the +belongs_to+ associations, and allows you to specify
-
# a target *label* for the *association* (monkey: george) rather than
-
# a target *id* for the *FK* (<tt>monkey_id: 1</tt>).
-
#
-
# ==== Polymorphic belongs_to
-
#
-
# Supporting polymorphic relationships is a little bit more complicated, since
-
# Active Record needs to know what type your association is pointing at. Something
-
# like this should look familiar:
-
#
-
# ### in fruit.rb
-
#
-
# belongs_to :eater, :polymorphic => true
-
#
-
# ### in fruits.yml
-
#
-
# apple:
-
# id: 1
-
# name: apple
-
# eater_id: 1
-
# eater_type: Monkey
-
#
-
# Can we do better? You bet!
-
#
-
# apple:
-
# eater: george (Monkey)
-
#
-
# Just provide the polymorphic target type and Active Record will take care of the rest.
-
#
-
# === has_and_belongs_to_many
-
#
-
# Time to give our monkey some fruit.
-
#
-
# ### in monkeys.yml
-
#
-
# george:
-
# id: 1
-
# name: George the Monkey
-
#
-
# ### in fruits.yml
-
#
-
# apple:
-
# id: 1
-
# name: apple
-
#
-
# orange:
-
# id: 2
-
# name: orange
-
#
-
# grape:
-
# id: 3
-
# name: grape
-
#
-
# ### in fruits_monkeys.yml
-
#
-
# apple_george:
-
# fruit_id: 1
-
# monkey_id: 1
-
#
-
# orange_george:
-
# fruit_id: 2
-
# monkey_id: 1
-
#
-
# grape_george:
-
# fruit_id: 3
-
# monkey_id: 1
-
#
-
# Let's make the HABTM fixture go away.
-
#
-
# ### in monkeys.yml
-
#
-
# george:
-
# id: 1
-
# name: George the Monkey
-
# fruits: apple, orange, grape
-
#
-
# ### in fruits.yml
-
#
-
# apple:
-
# name: apple
-
#
-
# orange:
-
# name: orange
-
#
-
# grape:
-
# name: grape
-
#
-
# Zap! No more fruits_monkeys.yml file. We've specified the list of fruits
-
# on George's fixture, but we could've just as easily specified a list
-
# of monkeys on each fruit. As with +belongs_to+, Active Record reflects on
-
# the fixture's model class and discovers the +has_and_belongs_to_many+
-
# associations.
-
#
-
# == Autofilled timestamp columns
-
#
-
# If your table/model specifies any of Active Record's
-
# standard timestamp columns (+created_at+, +created_on+, +updated_at+, +updated_on+),
-
# they will automatically be set to <tt>Time.now</tt>.
-
#
-
# If you've set specific values, they'll be left alone.
-
#
-
# == Fixture label interpolation
-
#
-
# The label of the current fixture is always available as a column value:
-
#
-
# geeksomnia:
-
# name: Geeksomnia's Account
-
# subdomain: $LABEL
-
#
-
# Also, sometimes (like when porting older join table fixtures) you'll need
-
# to be able to get a hold of the identifier for a given label. ERB
-
# to the rescue:
-
#
-
# george_reginald:
-
# monkey_id: <%= ActiveRecord::Fixtures.identify(:reginald) %>
-
# pirate_id: <%= ActiveRecord::Fixtures.identify(:george) %>
-
#
-
# == Support for YAML defaults
-
#
-
# You probably already know how to use YAML to set and reuse defaults in
-
# your <tt>database.yml</tt> file. You can use the same technique in your fixtures:
-
#
-
# DEFAULTS: &DEFAULTS
-
# created_on: <%= 3.weeks.ago.to_s(:db) %>
-
#
-
# first:
-
# name: Smurf
-
# <<: *DEFAULTS
-
#
-
# second:
-
# name: Fraggle
-
# <<: *DEFAULTS
-
#
-
# Any fixture labeled "DEFAULTS" is safely ignored.
-
-
1
Fixture = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Fixture', 'ActiveRecord::Fixture')
-
1
Fixtures = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Fixtures', 'ActiveRecord::Fixtures')
-
-
1
module ActiveRecord
-
1
class Fixtures
-
1
MAX_ID = 2 ** 30 - 1
-
-
1
@@all_cached_fixtures = Hash.new { |h,k| h[k] = {} }
-
-
1
def self.find_table_name(table_name) # :nodoc:
-
ActiveRecord::Base.pluralize_table_names ?
-
table_name.to_s.singularize.camelize :
-
table_name.to_s.camelize
-
end
-
-
1
def self.reset_cache
-
@@all_cached_fixtures.clear
-
end
-
-
1
def self.cache_for_connection(connection)
-
@@all_cached_fixtures[connection]
-
end
-
-
1
def self.fixture_is_cached?(connection, table_name)
-
cache_for_connection(connection)[table_name]
-
end
-
-
1
def self.cached_fixtures(connection, keys_to_fetch = nil)
-
if keys_to_fetch
-
cache_for_connection(connection).values_at(*keys_to_fetch)
-
else
-
cache_for_connection(connection).values
-
end
-
end
-
-
1
def self.cache_fixtures(connection, fixtures_map)
-
cache_for_connection(connection).update(fixtures_map)
-
end
-
-
1
def self.instantiate_fixtures(object, fixture_name, fixtures, load_instances = true)
-
if load_instances
-
fixtures.each do |name, fixture|
-
begin
-
object.instance_variable_set "@#{name}", fixture.find
-
rescue FixtureClassNotFound
-
nil
-
end
-
end
-
end
-
end
-
-
1
def self.instantiate_all_loaded_fixtures(object, load_instances = true)
-
all_loaded_fixtures.each do |table_name, fixtures|
-
ActiveRecord::Fixtures.instantiate_fixtures(object, table_name, fixtures, load_instances)
-
end
-
end
-
-
1
cattr_accessor :all_loaded_fixtures
-
1
self.all_loaded_fixtures = {}
-
-
1
def self.create_fixtures(fixtures_directory, table_names, class_names = {})
-
table_names = [table_names].flatten.map { |n| n.to_s }
-
table_names.each { |n|
-
class_names[n.tr('/', '_').to_sym] = n.classify if n.include?('/')
-
}
-
-
# FIXME: Apparently JK uses this.
-
connection = block_given? ? yield : ActiveRecord::Base.connection
-
-
files_to_read = table_names.reject { |table_name|
-
fixture_is_cached?(connection, table_name)
-
}
-
-
unless files_to_read.empty?
-
connection.disable_referential_integrity do
-
fixtures_map = {}
-
-
fixture_files = files_to_read.map do |path|
-
table_name = path.tr '/', '_'
-
-
fixtures_map[path] = ActiveRecord::Fixtures.new(
-
connection,
-
table_name,
-
class_names[table_name.to_sym] || table_name.classify,
-
File.join(fixtures_directory, path))
-
end
-
-
all_loaded_fixtures.update(fixtures_map)
-
-
connection.transaction(:requires_new => true) do
-
fixture_files.each do |ff|
-
conn = ff.model_class.respond_to?(:connection) ? ff.model_class.connection : connection
-
table_rows = ff.table_rows
-
-
table_rows.keys.each do |table|
-
conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete'
-
end
-
-
table_rows.each do |table_name,rows|
-
rows.each do |row|
-
conn.insert_fixture(row, table_name)
-
end
-
end
-
end
-
-
# Cap primary key sequences to max(pk).
-
if connection.respond_to?(:reset_pk_sequence!)
-
table_names.each do |table_name|
-
connection.reset_pk_sequence!(table_name.tr('/', '_'))
-
end
-
end
-
end
-
-
cache_fixtures(connection, fixtures_map)
-
end
-
end
-
cached_fixtures(connection, table_names)
-
end
-
-
# Returns a consistent, platform-independent identifier for +label+.
-
# Identifiers are positive integers less than 2^32.
-
1
def self.identify(label)
-
Zlib.crc32(label.to_s) % MAX_ID
-
end
-
-
1
attr_reader :table_name, :name, :fixtures, :model_class
-
-
1
def initialize(connection, table_name, class_name, fixture_path)
-
@connection = connection
-
@table_name = table_name
-
@fixture_path = fixture_path
-
@name = table_name # preserve fixture base name
-
@class_name = class_name
-
-
@fixtures = ActiveSupport::OrderedHash.new
-
@table_name = "#{ActiveRecord::Base.table_name_prefix}#{@table_name}#{ActiveRecord::Base.table_name_suffix}"
-
-
# Should be an AR::Base type class
-
if class_name.is_a?(Class)
-
@table_name = class_name.table_name
-
@connection = class_name.connection
-
@model_class = class_name
-
else
-
@model_class = class_name.constantize rescue nil
-
end
-
-
read_fixture_files
-
end
-
-
1
def [](x)
-
fixtures[x]
-
end
-
-
1
def []=(k,v)
-
fixtures[k] = v
-
end
-
-
1
def each(&block)
-
fixtures.each(&block)
-
end
-
-
1
def size
-
fixtures.size
-
end
-
-
# Return a hash of rows to be inserted. The key is the table, the value is
-
# a list of rows to insert to that table.
-
1
def table_rows
-
now = ActiveRecord::Base.default_timezone == :utc ? Time.now.utc : Time.now
-
now = now.to_s(:db)
-
-
# allow a standard key to be used for doing defaults in YAML
-
fixtures.delete('DEFAULTS')
-
-
# track any join tables we need to insert later
-
rows = Hash.new { |h,table| h[table] = [] }
-
-
rows[table_name] = fixtures.map do |label, fixture|
-
row = fixture.to_hash
-
-
if model_class && model_class < ActiveRecord::Base
-
# fill in timestamp columns if they aren't specified and the model is set to record_timestamps
-
if model_class.record_timestamps
-
timestamp_column_names.each do |name|
-
row[name] = now unless row.key?(name)
-
end
-
end
-
-
# interpolate the fixture label
-
row.each do |key, value|
-
row[key] = label if value == "$LABEL"
-
end
-
-
# generate a primary key if necessary
-
if has_primary_key_column? && !row.include?(primary_key_name)
-
row[primary_key_name] = ActiveRecord::Fixtures.identify(label)
-
end
-
-
# If STI is used, find the correct subclass for association reflection
-
reflection_class =
-
if row.include?(inheritance_column_name)
-
row[inheritance_column_name].constantize rescue model_class
-
else
-
model_class
-
end
-
-
reflection_class.reflect_on_all_associations.each do |association|
-
case association.macro
-
when :belongs_to
-
# Do not replace association name with association foreign key if they are named the same
-
fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
-
-
if association.name.to_s != fk_name && value = row.delete(association.name.to_s)
-
if association.options[:polymorphic] && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
-
# support polymorphic belongs_to as "label (Type)"
-
row[association.foreign_type] = $1
-
end
-
-
row[fk_name] = ActiveRecord::Fixtures.identify(value)
-
end
-
when :has_and_belongs_to_many
-
if (targets = row.delete(association.name.to_s))
-
targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
-
table_name = association.options[:join_table]
-
rows[table_name].concat targets.map { |target|
-
{ association.foreign_key => row[primary_key_name],
-
association.association_foreign_key => ActiveRecord::Fixtures.identify(target) }
-
}
-
end
-
end
-
end
-
end
-
-
row
-
end
-
rows
-
end
-
-
1
private
-
1
def primary_key_name
-
@primary_key_name ||= model_class && model_class.primary_key
-
end
-
-
1
def has_primary_key_column?
-
@has_primary_key_column ||= primary_key_name &&
-
model_class.columns.any? { |c| c.name == primary_key_name }
-
end
-
-
1
def timestamp_column_names
-
@timestamp_column_names ||=
-
%w(created_at created_on updated_at updated_on) & column_names
-
end
-
-
1
def inheritance_column_name
-
@inheritance_column_name ||= model_class && model_class.inheritance_column
-
end
-
-
1
def column_names
-
@column_names ||= @connection.columns(@table_name).collect { |c| c.name }
-
end
-
-
1
def read_fixture_files
-
if File.file?(yaml_file_path)
-
read_yaml_fixture_files
-
elsif File.file?(csv_file_path)
-
read_csv_fixture_files
-
else
-
raise FixturesFileNotFound, "Could not find #{yaml_file_path} or #{csv_file_path}"
-
end
-
end
-
-
1
def read_yaml_fixture_files
-
yaml_string = (Dir["#{@fixture_path}/**/*.yml"].select { |f|
-
File.file?(f)
-
} + [yaml_file_path]).map { |file_path| IO.read(file_path) }.join
-
-
if yaml = parse_yaml_string(yaml_string)
-
# If the file is an ordered map, extract its children.
-
yaml_value =
-
if yaml.respond_to?(:type_id) && yaml.respond_to?(:value)
-
yaml.value
-
else
-
[yaml]
-
end
-
-
yaml_value.each do |fixture|
-
raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{fixture}" unless fixture.respond_to?(:each)
-
fixture.each do |name, data|
-
unless data
-
raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{name} (nil)"
-
end
-
-
fixtures[name] = ActiveRecord::Fixture.new(data, model_class)
-
end
-
end
-
end
-
end
-
-
1
def read_csv_fixture_files
-
reader = CSV.parse(erb_render(IO.read(csv_file_path)))
-
header = reader.shift
-
i = 0
-
reader.each do |row|
-
data = {}
-
row.each_with_index { |cell, j| data[header[j].to_s.strip] = cell.to_s.strip }
-
fixtures["#{@class_name.to_s.underscore}_#{i+=1}"] = ActiveRecord::Fixture.new(data, model_class)
-
end
-
end
-
1
deprecate :read_csv_fixture_files
-
-
1
def yaml_file_path
-
"#{@fixture_path}.yml"
-
end
-
-
1
def csv_file_path
-
@fixture_path + ".csv"
-
end
-
-
1
def yaml_fixtures_key(path)
-
File.basename(@fixture_path).split(".").first
-
end
-
-
1
def parse_yaml_string(fixture_content)
-
YAML::load(erb_render(fixture_content))
-
rescue => error
-
raise Fixture::FormatError, "a YAML error occurred parsing #{yaml_file_path}. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Please have a look at http://www.yaml.org/faq.html\nThe exact error was:\n #{error.class}: #{error}"
-
end
-
-
1
def erb_render(fixture_content)
-
ERB.new(fixture_content).result
-
end
-
end
-
-
1
class Fixture #:nodoc:
-
1
include Enumerable
-
-
1
class FixtureError < StandardError #:nodoc:
-
end
-
-
1
class FormatError < FixtureError #:nodoc:
-
end
-
-
1
attr_reader :model_class, :fixture
-
-
1
def initialize(fixture, model_class)
-
@fixture = fixture
-
@model_class = model_class
-
end
-
-
1
def class_name
-
model_class.name if model_class
-
end
-
-
1
def each
-
fixture.each { |item| yield item }
-
end
-
-
1
def [](key)
-
fixture[key]
-
end
-
-
1
alias :to_hash :fixture
-
-
1
def find
-
if model_class
-
model_class.find(fixture[model_class.primary_key])
-
else
-
raise FixtureClassNotFound, "No class attached to find."
-
end
-
end
-
end
-
end
-
-
1
module ActiveRecord
-
1
module TestFixtures
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
setup :setup_fixtures
-
1
teardown :teardown_fixtures
-
-
1
class_attribute :fixture_path
-
1
class_attribute :fixture_table_names
-
1
class_attribute :fixture_class_names
-
1
class_attribute :use_transactional_fixtures
-
1
class_attribute :use_instantiated_fixtures # true, false, or :no_instances
-
1
class_attribute :pre_loaded_fixtures
-
-
1
self.fixture_table_names = []
-
1
self.use_transactional_fixtures = true
-
1
self.use_instantiated_fixtures = false
-
1
self.pre_loaded_fixtures = false
-
-
1
self.fixture_class_names = Hash.new do |h, table_name|
-
h[table_name] = ActiveRecord::Fixtures.find_table_name(table_name)
-
end
-
end
-
-
1
module ClassMethods
-
1
def set_fixture_class(class_names = {})
-
self.fixture_class_names = self.fixture_class_names.merge(class_names)
-
end
-
-
1
def fixtures(*fixture_names)
-
if fixture_names.first == :all
-
fixture_names = Dir["#{fixture_path}/**/*.{yml,csv}"]
-
fixture_names.map! { |f| f[(fixture_path.size + 1)..-5] }
-
else
-
fixture_names = fixture_names.flatten.map { |n| n.to_s }
-
end
-
-
self.fixture_table_names |= fixture_names
-
require_fixture_classes(fixture_names)
-
setup_fixture_accessors(fixture_names)
-
end
-
-
1
def try_to_load_dependency(file_name)
-
require_dependency file_name
-
rescue LoadError => e
-
# Let's hope the developer has included it himself
-
-
# Let's warn in case this is a subdependency, otherwise
-
# subdependency error messages are totally cryptic
-
if ActiveRecord::Base.logger
-
ActiveRecord::Base.logger.warn("Unable to load #{file_name}, underlying cause #{e.message} \n\n #{e.backtrace.join("\n")}")
-
end
-
end
-
-
1
def require_fixture_classes(fixture_names = nil)
-
(fixture_names || fixture_table_names).each do |fixture_name|
-
file_name = fixture_name.to_s
-
file_name = file_name.singularize if ActiveRecord::Base.pluralize_table_names
-
try_to_load_dependency(file_name)
-
end
-
end
-
-
1
def setup_fixture_accessors(fixture_names = nil)
-
fixture_names = Array.wrap(fixture_names || fixture_table_names)
-
methods = Module.new do
-
fixture_names.each do |fixture_name|
-
fixture_name = fixture_name.to_s.tr('./', '_')
-
-
define_method(fixture_name) do |*fixtures|
-
force_reload = fixtures.pop if fixtures.last == true || fixtures.last == :reload
-
-
@fixture_cache[fixture_name] ||= {}
-
-
instances = fixtures.map do |fixture|
-
@fixture_cache[fixture_name].delete(fixture) if force_reload
-
-
if @loaded_fixtures[fixture_name][fixture.to_s]
-
ActiveRecord::IdentityMap.without do
-
@fixture_cache[fixture_name][fixture] ||= @loaded_fixtures[fixture_name][fixture.to_s].find
-
end
-
else
-
raise StandardError, "No fixture with name '#{fixture}' found for table '#{fixture_name}'"
-
end
-
end
-
-
instances.size == 1 ? instances.first : instances
-
end
-
private fixture_name
-
end
-
end
-
include methods
-
end
-
-
1
def uses_transaction(*methods)
-
@uses_transaction = [] unless defined?(@uses_transaction)
-
@uses_transaction.concat methods.map { |m| m.to_s }
-
end
-
-
1
def uses_transaction?(method)
-
@uses_transaction = [] unless defined?(@uses_transaction)
-
@uses_transaction.include?(method.to_s)
-
end
-
end
-
-
1
def run_in_transaction?
-
use_transactional_fixtures &&
-
!self.class.uses_transaction?(method_name)
-
end
-
-
1
def setup_fixtures
-
return unless !ActiveRecord::Base.configurations.blank?
-
-
if pre_loaded_fixtures && !use_transactional_fixtures
-
raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
-
end
-
-
@fixture_cache = {}
-
@@already_loaded_fixtures ||= {}
-
-
# Load fixtures once and begin transaction.
-
if run_in_transaction?
-
if @@already_loaded_fixtures[self.class]
-
@loaded_fixtures = @@already_loaded_fixtures[self.class]
-
else
-
@loaded_fixtures = load_fixtures
-
@@already_loaded_fixtures[self.class] = @loaded_fixtures
-
end
-
ActiveRecord::Base.connection.increment_open_transactions
-
ActiveRecord::Base.connection.transaction_joinable = false
-
ActiveRecord::Base.connection.begin_db_transaction
-
# Load fixtures for every test.
-
else
-
ActiveRecord::Fixtures.reset_cache
-
@@already_loaded_fixtures[self.class] = nil
-
@loaded_fixtures = load_fixtures
-
end
-
-
# Instantiate fixtures for every test if requested.
-
instantiate_fixtures if use_instantiated_fixtures
-
end
-
-
1
def teardown_fixtures
-
return unless defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
-
-
unless run_in_transaction?
-
ActiveRecord::Fixtures.reset_cache
-
end
-
-
# Rollback changes if a transaction is active.
-
if run_in_transaction? && ActiveRecord::Base.connection.open_transactions != 0
-
ActiveRecord::Base.connection.rollback_db_transaction
-
ActiveRecord::Base.connection.decrement_open_transactions
-
end
-
ActiveRecord::Base.clear_active_connections!
-
end
-
-
1
private
-
1
def load_fixtures
-
fixtures = ActiveRecord::Fixtures.create_fixtures(fixture_path, fixture_table_names, fixture_class_names)
-
Hash[fixtures.map { |f| [f.name, f] }]
-
end
-
-
# for pre_loaded_fixtures, only require the classes once. huge speed improvement
-
1
@@required_fixture_classes = false
-
-
1
def instantiate_fixtures
-
if pre_loaded_fixtures
-
raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::Fixtures.all_loaded_fixtures.empty?
-
unless @@required_fixture_classes
-
self.class.require_fixture_classes ActiveRecord::Fixtures.all_loaded_fixtures.keys
-
@@required_fixture_classes = true
-
end
-
ActiveRecord::Fixtures.instantiate_all_loaded_fixtures(self, load_instances?)
-
else
-
raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
-
@loaded_fixtures.each do |fixture_name, fixtures|
-
ActiveRecord::Fixtures.instantiate_fixtures(self, fixture_name, fixtures, load_instances?)
-
end
-
end
-
end
-
-
1
def load_instances?
-
use_instantiated_fixtures != :no_instances
-
end
-
end
-
end
-
1
module ActiveRecord
-
# = Active Record Identity Map
-
#
-
# Ensures that each object gets loaded only once by keeping every loaded
-
# object in a map. Looks up objects using the map when referring to them.
-
#
-
# More information on Identity Map pattern:
-
# http://www.martinfowler.com/eaaCatalog/identityMap.html
-
#
-
# == Configuration
-
#
-
# In order to enable IdentityMap, set <tt>config.active_record.identity_map = true</tt>
-
# in your <tt>config/application.rb</tt> file.
-
#
-
# IdentityMap is disabled by default and still in development (i.e. use it with care).
-
#
-
# == Associations
-
#
-
# Active Record Identity Map does not track associations yet. For example:
-
#
-
# comment = @post.comments.first
-
# comment.post = nil
-
# @post.comments.include?(comment) #=> true
-
#
-
# Ideally, the example above would return false, removing the comment object from the
-
# post association when the association is nullified. This may cause side effects, as
-
# in the situation below, if Identity Map is enabled:
-
#
-
# Post.has_many :comments, :dependent => :destroy
-
#
-
# comment = @post.comments.first
-
# comment.post = nil
-
# comment.save
-
# Post.destroy(@post.id)
-
#
-
# Without using Identity Map, the code above will destroy the @post object leaving
-
# the comment object intact. However, once we enable Identity Map, the post loaded
-
# by Post.destroy is exactly the same object as the object @post. As the object @post
-
# still has the comment object in @post.comments, once Identity Map is enabled, the
-
# comment object will be accidently removed.
-
#
-
# This inconsistency is meant to be fixed in future Rails releases.
-
#
-
1
module IdentityMap
-
-
1
class << self
-
1
def enabled=(flag)
-
Thread.current[:identity_map_enabled] = flag
-
end
-
-
1
def enabled
-
Thread.current[:identity_map_enabled]
-
end
-
1
alias enabled? enabled
-
-
1
def repository
-
Thread.current[:identity_map] ||= Hash.new { |h,k| h[k] = {} }
-
end
-
-
1
def use
-
old, self.enabled = enabled, true
-
-
yield if block_given?
-
ensure
-
self.enabled = old
-
clear
-
end
-
-
1
def without
-
old, self.enabled = enabled, false
-
-
yield if block_given?
-
ensure
-
self.enabled = old
-
end
-
-
1
def get(klass, primary_key)
-
record = repository[klass.symbolized_sti_name][primary_key]
-
-
if record.is_a?(klass)
-
ActiveSupport::Notifications.instrument("identity.active_record",
-
:line => "From Identity Map (id: #{primary_key})",
-
:name => "#{klass} Loaded",
-
:connection_id => object_id)
-
-
record
-
else
-
nil
-
end
-
end
-
-
1
def add(record)
-
repository[record.class.symbolized_sti_name][record.id] = record
-
end
-
-
1
def remove(record)
-
repository[record.class.symbolized_sti_name].delete(record.id)
-
end
-
-
1
def remove_by_id(symbolized_sti_name, id)
-
repository[symbolized_sti_name].delete(id)
-
end
-
-
1
def clear
-
repository.clear
-
end
-
end
-
-
# Reinitialize an Identity Map model object from +coder+.
-
# +coder+ must contain the attributes necessary for initializing an empty
-
# model object.
-
1
def reinit_with(coder)
-
@attributes_cache = {}
-
dirty = @changed_attributes.keys
-
@attributes.update(coder['attributes'].except(*dirty))
-
@changed_attributes.update(coder['attributes'].slice(*dirty))
-
@changed_attributes.delete_if{|k,v| v.eql? @attributes[k]}
-
-
set_serialized_attributes
-
-
run_callbacks :find
-
-
self
-
end
-
-
1
class Middleware
-
1
class Body #:nodoc:
-
1
def initialize(target, original)
-
@target = target
-
@original = original
-
end
-
-
1
def each(&block)
-
@target.each(&block)
-
end
-
-
1
def close
-
@target.close if @target.respond_to?(:close)
-
ensure
-
IdentityMap.enabled = @original
-
IdentityMap.clear
-
end
-
end
-
-
1
def initialize(app)
-
@app = app
-
end
-
-
1
def call(env)
-
enabled = IdentityMap.enabled
-
IdentityMap.enabled = true
-
status, headers, body = @app.call(env)
-
[status, headers, Body.new(body, enabled)]
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module Locking
-
# == What is Optimistic Locking
-
#
-
# Optimistic locking allows multiple users to access the same record for edits, and assumes a minimum of
-
# conflicts with the data. It does this by checking whether another process has made changes to a record since
-
# it was opened, an <tt>ActiveRecord::StaleObjectError</tt> exception is thrown if that has occurred
-
# and the update is ignored.
-
#
-
# Check out <tt>ActiveRecord::Locking::Pessimistic</tt> for an alternative.
-
#
-
# == Usage
-
#
-
# Active Records support optimistic locking if the field +lock_version+ is present. Each update to the
-
# record increments the +lock_version+ column and the locking facilities ensure that records instantiated twice
-
# will let the last one saved raise a +StaleObjectError+ if the first was also updated. Example:
-
#
-
# p1 = Person.find(1)
-
# p2 = Person.find(1)
-
#
-
# p1.first_name = "Michael"
-
# p1.save
-
#
-
# p2.first_name = "should fail"
-
# p2.save # Raises a ActiveRecord::StaleObjectError
-
#
-
# Optimistic locking will also check for stale data when objects are destroyed. Example:
-
#
-
# p1 = Person.find(1)
-
# p2 = Person.find(1)
-
#
-
# p1.first_name = "Michael"
-
# p1.save
-
#
-
# p2.destroy # Raises a ActiveRecord::StaleObjectError
-
#
-
# You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging,
-
# or otherwise apply the business logic needed to resolve the conflict.
-
#
-
# You must ensure that your database schema defaults the +lock_version+ column to 0.
-
#
-
# This behavior can be turned off by setting <tt>ActiveRecord::Base.lock_optimistically = false</tt>.
-
# To override the name of the +lock_version+ column, invoke the <tt>set_locking_column</tt> method.
-
# This method uses the same syntax as <tt>set_table_name</tt>
-
1
module Optimistic
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
cattr_accessor :lock_optimistically, :instance_writer => false
-
1
self.lock_optimistically = true
-
-
1
class << self
-
1
alias_method :locking_column=, :set_locking_column
-
end
-
end
-
-
1
def locking_enabled? #:nodoc:
-
self.class.locking_enabled?
-
end
-
-
1
private
-
1
def increment_lock
-
lock_col = self.class.locking_column
-
previous_lock_value = send(lock_col).to_i
-
send(lock_col + '=', previous_lock_value + 1)
-
end
-
-
1
def attributes_from_column_definition
-
3
result = super
-
-
# If the locking column has no default value set,
-
# start the lock version at zero. Note we can't use
-
# <tt>locking_enabled?</tt> at this point as
-
# <tt>@attributes</tt> may not have been initialized yet.
-
-
3
if result.key?(self.class.locking_column) && lock_optimistically
-
result[self.class.locking_column] ||= 0
-
end
-
-
3
result
-
end
-
-
1
def update(attribute_names = @attributes.keys) #:nodoc:
-
return super unless locking_enabled?
-
return 0 if attribute_names.empty?
-
-
lock_col = self.class.locking_column
-
previous_lock_value = send(lock_col).to_i
-
increment_lock
-
-
attribute_names += [lock_col]
-
attribute_names.uniq!
-
-
begin
-
relation = self.class.unscoped
-
-
stmt = relation.where(
-
relation.table[self.class.primary_key].eq(id).and(
-
relation.table[lock_col].eq(quote_value(previous_lock_value))
-
)
-
).arel.compile_update(arel_attributes_values(false, false, attribute_names))
-
-
affected_rows = connection.update stmt
-
-
unless affected_rows == 1
-
raise ActiveRecord::StaleObjectError, "Attempted to update a stale object: #{self.class.name}"
-
end
-
-
affected_rows
-
-
# If something went wrong, revert the version.
-
rescue Exception
-
send(lock_col + '=', previous_lock_value)
-
raise
-
end
-
end
-
-
1
def destroy #:nodoc:
-
return super unless locking_enabled?
-
-
if persisted?
-
table = self.class.arel_table
-
lock_col = self.class.locking_column
-
predicate = table[self.class.primary_key].eq(id).
-
and(table[lock_col].eq(send(lock_col).to_i))
-
-
affected_rows = self.class.unscoped.where(predicate).delete_all
-
-
unless affected_rows == 1
-
raise ActiveRecord::StaleObjectError, "Attempted to delete a stale object: #{self.class.name}"
-
end
-
end
-
-
@destroyed = true
-
freeze
-
end
-
-
1
module ClassMethods
-
1
DEFAULT_LOCKING_COLUMN = 'lock_version'
-
-
# Returns true if the +lock_optimistically+ flag is set to true
-
# (which it is, by default) and the table includes the
-
# +locking_column+ column (defaults to +lock_version+).
-
1
def locking_enabled?
-
lock_optimistically && columns_hash[locking_column]
-
end
-
-
# Set the column to use for optimistic locking. Defaults to +lock_version+.
-
1
def set_locking_column(value = nil, &block)
-
1
define_attr_method :locking_column, value, &block
-
1
value
-
end
-
-
# The version column used for optimistic locking. Defaults to +lock_version+.
-
1
def locking_column
-
1
reset_locking_column
-
end
-
-
# Quote the column name used for optimistic locking.
-
1
def quoted_locking_column
-
connection.quote_column_name(locking_column)
-
end
-
-
# Reset the column used for optimistic locking back to the +lock_version+ default.
-
1
def reset_locking_column
-
1
set_locking_column DEFAULT_LOCKING_COLUMN
-
end
-
-
# Make sure the lock version column gets updated when counters are
-
# updated.
-
1
def update_counters(id, counters)
-
counters = counters.merge(locking_column => 1) if locking_enabled?
-
super
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module Locking
-
# Locking::Pessimistic provides support for row-level locking using
-
# SELECT ... FOR UPDATE and other lock types.
-
#
-
# Pass <tt>:lock => true</tt> to <tt>ActiveRecord::Base.find</tt> to obtain an exclusive
-
# lock on the selected rows:
-
# # select * from accounts where id=1 for update
-
# Account.find(1, :lock => true)
-
#
-
# Pass <tt>:lock => 'some locking clause'</tt> to give a database-specific locking clause
-
# of your own such as 'LOCK IN SHARE MODE' or 'FOR UPDATE NOWAIT'. Example:
-
#
-
# Account.transaction do
-
# # select * from accounts where name = 'shugo' limit 1 for update
-
# shugo = Account.where("name = 'shugo'").lock(true).first
-
# yuko = Account.where("name = 'shugo'").lock(true).first
-
# shugo.balance -= 100
-
# shugo.save!
-
# yuko.balance += 100
-
# yuko.save!
-
# end
-
#
-
# You can also use <tt>ActiveRecord::Base#lock!</tt> method to lock one record by id.
-
# This may be better if you don't need to lock every row. Example:
-
#
-
# Account.transaction do
-
# # select * from accounts where ...
-
# accounts = Account.where(...).all
-
# account1 = accounts.detect { |account| ... }
-
# account2 = accounts.detect { |account| ... }
-
# # select * from accounts where id=? for update
-
# account1.lock!
-
# account2.lock!
-
# account1.balance -= 100
-
# account1.save!
-
# account2.balance += 100
-
# account2.save!
-
# end
-
#
-
# Database-specific information on row locking:
-
# MySQL: http://dev.mysql.com/doc/refman/5.1/en/innodb-locking-reads.html
-
# PostgreSQL: http://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
-
1
module Pessimistic
-
# Obtain a row lock on this record. Reloads the record to obtain the requested
-
# lock. Pass an SQL locking clause to append the end of the SELECT statement
-
# or pass true for "FOR UPDATE" (the default, an exclusive row lock). Returns
-
# the locked record.
-
1
def lock!(lock = true)
-
reload(:lock => lock) if persisted?
-
self
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
class LogSubscriber < ActiveSupport::LogSubscriber
-
1
def self.runtime=(value)
-
9
Thread.current["active_record_sql_runtime"] = value
-
end
-
-
1
def self.runtime
-
9
Thread.current["active_record_sql_runtime"] ||= 0
-
end
-
-
1
def self.reset_runtime
-
rt, self.runtime = runtime, 0
-
rt
-
end
-
-
1
def initialize
-
1
super
-
1
@odd_or_even = false
-
end
-
-
1
def sql(event)
-
9
self.class.runtime += event.duration
-
9
return unless logger.debug?
-
-
9
payload = event.payload
-
-
9
return if 'SCHEMA' == payload[:name]
-
-
name = '%s (%.1fms)' % [payload[:name], event.duration]
-
sql = payload[:sql].squeeze(' ')
-
binds = nil
-
-
unless (payload[:binds] || []).empty?
-
binds = " " + payload[:binds].map { |col,v|
-
[col.name, v]
-
}.inspect
-
end
-
-
if odd?
-
name = color(name, CYAN, true)
-
sql = color(sql, nil, true)
-
else
-
name = color(name, MAGENTA, true)
-
end
-
-
debug " #{name} #{sql}#{binds}"
-
end
-
-
1
def identity(event)
-
return unless logger.debug?
-
-
name = color(event.payload[:name], odd? ? CYAN : MAGENTA, true)
-
line = odd? ? color(event.payload[:line], nil, true) : event.payload[:line]
-
-
debug " #{name} #{line}"
-
end
-
-
1
def odd?
-
@odd_or_even = !@odd_or_even
-
end
-
-
1
def logger
-
18
ActiveRecord::Base.logger
-
end
-
end
-
end
-
-
1
ActiveRecord::LogSubscriber.attach_to :active_record
-
1
require 'active_support/core_ext/array'
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/class/attribute'
-
-
1
module ActiveRecord
-
# = Active Record Named \Scopes
-
1
module NamedScope
-
1
extend ActiveSupport::Concern
-
-
1
module ClassMethods
-
# Returns an anonymous \scope.
-
#
-
# posts = Post.scoped
-
# posts.size # Fires "select count(*) from posts" and returns the count
-
# posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects
-
#
-
# fruits = Fruit.scoped
-
# fruits = fruits.where(:colour => 'red') if options[:red_only]
-
# fruits = fruits.limit(10) if limited?
-
#
-
# Anonymous \scopes tend to be useful when procedurally generating complex
-
# queries, where passing intermediate values (\scopes) around as first-class
-
# objects is convenient.
-
#
-
# You can define a \scope that applies to all finders using
-
# ActiveRecord::Base.default_scope.
-
1
def scoped(options = nil)
-
if options
-
scoped.apply_finder_options(options)
-
else
-
if current_scope
-
current_scope.clone
-
else
-
scope = relation.clone
-
scope.default_scoped = true
-
scope
-
end
-
end
-
end
-
-
##
-
# Collects attributes from scopes that should be applied when creating
-
# an AR instance for the particular class this is called on.
-
1
def scope_attributes # :nodoc:
-
if current_scope
-
current_scope.scope_for_create
-
else
-
scope = relation.clone
-
scope.default_scoped = true
-
scope.scope_for_create
-
end
-
end
-
-
##
-
# Are there default attributes associated with this scope?
-
1
def scope_attributes? # :nodoc:
-
3
current_scope || default_scopes.any?
-
end
-
-
# Adds a class method for retrieving and querying objects. A \scope represents a narrowing of a database query,
-
# such as <tt>where(:color => :red).select('shirts.*').includes(:washing_instructions)</tt>.
-
#
-
# class Shirt < ActiveRecord::Base
-
# scope :red, where(:color => 'red')
-
# scope :dry_clean_only, joins(:washing_instructions).where('washing_instructions.dry_clean_only = ?', true)
-
# end
-
#
-
# The above calls to <tt>scope</tt> define class methods Shirt.red and Shirt.dry_clean_only. Shirt.red,
-
# in effect, represents the query <tt>Shirt.where(:color => 'red')</tt>.
-
#
-
# Note that this is simply 'syntactic sugar' for defining an actual class method:
-
#
-
# class Shirt < ActiveRecord::Base
-
# def self.red
-
# where(:color => 'red')
-
# end
-
# end
-
#
-
# Unlike <tt>Shirt.find(...)</tt>, however, the object returned by Shirt.red is not an Array; it
-
# resembles the association object constructed by a <tt>has_many</tt> declaration. For instance,
-
# you can invoke <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>, <tt>Shirt.red.where(:size => 'small')</tt>.
-
# Also, just as with the association objects, named \scopes act like an Array, implementing Enumerable;
-
# <tt>Shirt.red.each(&block)</tt>, <tt>Shirt.red.first</tt>, and <tt>Shirt.red.inject(memo, &block)</tt>
-
# all behave as if Shirt.red really was an Array.
-
#
-
# These named \scopes are composable. For instance, <tt>Shirt.red.dry_clean_only</tt> will produce
-
# all shirts that are both red and dry clean only.
-
# Nested finds and calculations also work with these compositions: <tt>Shirt.red.dry_clean_only.count</tt>
-
# returns the number of garments for which these criteria obtain. Similarly with
-
# <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
-
#
-
# All \scopes are available as class methods on the ActiveRecord::Base descendant upon which
-
# the \scopes were defined. But they are also available to <tt>has_many</tt> associations. If,
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :shirts
-
# end
-
#
-
# then <tt>elton.shirts.red.dry_clean_only</tt> will return all of Elton's red, dry clean
-
# only shirts.
-
#
-
# Named \scopes can also be procedural:
-
#
-
# class Shirt < ActiveRecord::Base
-
# scope :colored, lambda { |color| where(:color => color) }
-
# end
-
#
-
# In this example, <tt>Shirt.colored('puce')</tt> finds all puce shirts.
-
#
-
# On Ruby 1.9 you can use the 'stabby lambda' syntax:
-
#
-
# scope :colored, ->(color) { where(:color => color) }
-
#
-
# Note that scopes defined with \scope will be evaluated when they are defined, rather than
-
# when they are used. For example, the following would be incorrect:
-
#
-
# class Post < ActiveRecord::Base
-
# scope :recent, where('published_at >= ?', Time.now - 1.week)
-
# end
-
#
-
# The example above would be 'frozen' to the <tt>Time.now</tt> value when the <tt>Post</tt>
-
# class was defined, and so the resultant SQL query would always be the same. The correct
-
# way to do this would be via a lambda, which will re-evaluate the scope each time
-
# it is called:
-
#
-
# class Post < ActiveRecord::Base
-
# scope :recent, lambda { where('published_at >= ?', Time.now - 1.week) }
-
# end
-
#
-
# Named \scopes can also have extensions, just as with <tt>has_many</tt> declarations:
-
#
-
# class Shirt < ActiveRecord::Base
-
# scope :red, where(:color => 'red') do
-
# def dom_id
-
# 'red_shirts'
-
# end
-
# end
-
# end
-
#
-
# Scopes can also be used while creating/building a record.
-
#
-
# class Article < ActiveRecord::Base
-
# scope :published, where(:published => true)
-
# end
-
#
-
# Article.published.new.published # => true
-
# Article.published.create.published # => true
-
#
-
# Class methods on your model are automatically available
-
# on scopes. Assuming the following setup:
-
#
-
# class Article < ActiveRecord::Base
-
# scope :published, where(:published => true)
-
# scope :featured, where(:featured => true)
-
#
-
# def self.latest_article
-
# order('published_at desc').first
-
# end
-
#
-
# def self.titles
-
# map(&:title)
-
# end
-
#
-
# end
-
#
-
# We are able to call the methods like this:
-
#
-
# Article.published.featured.latest_article
-
# Article.featured.titles
-
-
1
def scope(name, scope_options = {})
-
name = name.to_sym
-
valid_scope_name?(name)
-
extension = Module.new(&Proc.new) if block_given?
-
-
scope_proc = lambda do |*args|
-
options = scope_options.respond_to?(:call) ? scope_options.call(*args) : scope_options
-
options = scoped.apply_finder_options(options) if options.is_a?(Hash)
-
-
relation = scoped.merge(options)
-
-
extension ? relation.extending(extension) : relation
-
end
-
-
singleton_class.send(:redefine_method, name, &scope_proc)
-
end
-
-
1
protected
-
-
1
def valid_scope_name?(name)
-
if respond_to?(name, true)
-
logger.warn "Creating scope :#{name}. " \
-
"Overwriting existing method #{self.name}.#{name}."
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/object/try'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
1
require 'active_support/core_ext/class/attribute'
-
-
1
module ActiveRecord
-
1
module NestedAttributes #:nodoc:
-
1
class TooManyRecords < ActiveRecordError
-
end
-
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
class_attribute :nested_attributes_options, :instance_writer => false
-
1
self.nested_attributes_options = {}
-
end
-
-
# = Active Record Nested Attributes
-
#
-
# Nested attributes allow you to save attributes on associated records
-
# through the parent. By default nested attribute updating is turned off,
-
# you can enable it using the accepts_nested_attributes_for class method.
-
# When you enable nested attributes an attribute writer is defined on
-
# the model.
-
#
-
# The attribute writer is named after the association, which means that
-
# in the following example, two new methods are added to your model:
-
#
-
# <tt>author_attributes=(attributes)</tt> and
-
# <tt>pages_attributes=(attributes)</tt>.
-
#
-
# class Book < ActiveRecord::Base
-
# has_one :author
-
# has_many :pages
-
#
-
# accepts_nested_attributes_for :author, :pages
-
# end
-
#
-
# Note that the <tt>:autosave</tt> option is automatically enabled on every
-
# association that accepts_nested_attributes_for is used for.
-
#
-
# === One-to-one
-
#
-
# Consider a Member model that has one Avatar:
-
#
-
# class Member < ActiveRecord::Base
-
# has_one :avatar
-
# accepts_nested_attributes_for :avatar
-
# end
-
#
-
# Enabling nested attributes on a one-to-one association allows you to
-
# create the member and avatar in one go:
-
#
-
# params = { :member => { :name => 'Jack', :avatar_attributes => { :icon => 'smiling' } } }
-
# member = Member.create(params[:member])
-
# member.avatar.id # => 2
-
# member.avatar.icon # => 'smiling'
-
#
-
# It also allows you to update the avatar through the member:
-
#
-
# params = { :member => { :avatar_attributes => { :id => '2', :icon => 'sad' } } }
-
# member.update_attributes params[:member]
-
# member.avatar.icon # => 'sad'
-
#
-
# By default you will only be able to set and update attributes on the
-
# associated model. If you want to destroy the associated model through the
-
# attributes hash, you have to enable it first using the
-
# <tt>:allow_destroy</tt> option.
-
#
-
# class Member < ActiveRecord::Base
-
# has_one :avatar
-
# accepts_nested_attributes_for :avatar, :allow_destroy => true
-
# end
-
#
-
# Now, when you add the <tt>_destroy</tt> key to the attributes hash, with a
-
# value that evaluates to +true+, you will destroy the associated model:
-
#
-
# member.avatar_attributes = { :id => '2', :_destroy => '1' }
-
# member.avatar.marked_for_destruction? # => true
-
# member.save
-
# member.reload.avatar # => nil
-
#
-
# Note that the model will _not_ be destroyed until the parent is saved.
-
#
-
# === One-to-many
-
#
-
# Consider a member that has a number of posts:
-
#
-
# class Member < ActiveRecord::Base
-
# has_many :posts
-
# accepts_nested_attributes_for :posts
-
# end
-
#
-
# You can now set or update attributes on an associated post model through
-
# the attribute hash.
-
#
-
# For each hash that does _not_ have an <tt>id</tt> key a new record will
-
# be instantiated, unless the hash also contains a <tt>_destroy</tt> key
-
# that evaluates to +true+.
-
#
-
# params = { :member => {
-
# :name => 'joe', :posts_attributes => [
-
# { :title => 'Kari, the awesome Ruby documentation browser!' },
-
# { :title => 'The egalitarian assumption of the modern citizen' },
-
# { :title => '', :_destroy => '1' } # this will be ignored
-
# ]
-
# }}
-
#
-
# member = Member.create(params['member'])
-
# member.posts.length # => 2
-
# member.posts.first.title # => 'Kari, the awesome Ruby documentation browser!'
-
# member.posts.second.title # => 'The egalitarian assumption of the modern citizen'
-
#
-
# You may also set a :reject_if proc to silently ignore any new record
-
# hashes if they fail to pass your criteria. For example, the previous
-
# example could be rewritten as:
-
#
-
# class Member < ActiveRecord::Base
-
# has_many :posts
-
# accepts_nested_attributes_for :posts, :reject_if => proc { |attributes| attributes['title'].blank? }
-
# end
-
#
-
# params = { :member => {
-
# :name => 'joe', :posts_attributes => [
-
# { :title => 'Kari, the awesome Ruby documentation browser!' },
-
# { :title => 'The egalitarian assumption of the modern citizen' },
-
# { :title => '' } # this will be ignored because of the :reject_if proc
-
# ]
-
# }}
-
#
-
# member = Member.create(params['member'])
-
# member.posts.length # => 2
-
# member.posts.first.title # => 'Kari, the awesome Ruby documentation browser!'
-
# member.posts.second.title # => 'The egalitarian assumption of the modern citizen'
-
#
-
# Alternatively, :reject_if also accepts a symbol for using methods:
-
#
-
# class Member < ActiveRecord::Base
-
# has_many :posts
-
# accepts_nested_attributes_for :posts, :reject_if => :new_record?
-
# end
-
#
-
# class Member < ActiveRecord::Base
-
# has_many :posts
-
# accepts_nested_attributes_for :posts, :reject_if => :reject_posts
-
#
-
# def reject_posts(attributed)
-
# attributed['title'].blank?
-
# end
-
# end
-
#
-
# If the hash contains an <tt>id</tt> key that matches an already
-
# associated record, the matching record will be modified:
-
#
-
# member.attributes = {
-
# :name => 'Joe',
-
# :posts_attributes => [
-
# { :id => 1, :title => '[UPDATED] An, as of yet, undisclosed awesome Ruby documentation browser!' },
-
# { :id => 2, :title => '[UPDATED] other post' }
-
# ]
-
# }
-
#
-
# member.posts.first.title # => '[UPDATED] An, as of yet, undisclosed awesome Ruby documentation browser!'
-
# member.posts.second.title # => '[UPDATED] other post'
-
#
-
# By default the associated records are protected from being destroyed. If
-
# you want to destroy any of the associated records through the attributes
-
# hash, you have to enable it first using the <tt>:allow_destroy</tt>
-
# option. This will allow you to also use the <tt>_destroy</tt> key to
-
# destroy existing records:
-
#
-
# class Member < ActiveRecord::Base
-
# has_many :posts
-
# accepts_nested_attributes_for :posts, :allow_destroy => true
-
# end
-
#
-
# params = { :member => {
-
# :posts_attributes => [{ :id => '2', :_destroy => '1' }]
-
# }}
-
#
-
# member.attributes = params['member']
-
# member.posts.detect { |p| p.id == 2 }.marked_for_destruction? # => true
-
# member.posts.length # => 2
-
# member.save
-
# member.reload.posts.length # => 1
-
#
-
# === Saving
-
#
-
# All changes to models, including the destruction of those marked for
-
# destruction, are saved and destroyed automatically and atomically when
-
# the parent model is saved. This happens inside the transaction initiated
-
# by the parents save method. See ActiveRecord::AutosaveAssociation.
-
#
-
# === Using with attr_accessible
-
#
-
# The use of <tt>attr_accessible</tt> can interfere with nested attributes
-
# if you're not careful. For example, if the <tt>Member</tt> model above
-
# was using <tt>attr_accessible</tt> like this:
-
#
-
# attr_accessible :name
-
#
-
# You would need to modify it to look like this:
-
#
-
# attr_accessible :name, :posts_attributes
-
#
-
# === Validating the presence of a parent model
-
#
-
# If you want to validate that a child record is associated with a parent
-
# record, you can use <tt>validates_presence_of</tt> and
-
# <tt>inverse_of</tt> as this example illustrates:
-
#
-
# class Member < ActiveRecord::Base
-
# has_many :posts, :inverse_of => :member
-
# accepts_nested_attributes_for :posts
-
# end
-
#
-
# class Post < ActiveRecord::Base
-
# belongs_to :member, :inverse_of => :posts
-
# validates_presence_of :member
-
# end
-
1
module ClassMethods
-
1
REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |_, value| value.blank? } }
-
-
# Defines an attributes writer for the specified association(s). If you
-
# are using <tt>attr_protected</tt> or <tt>attr_accessible</tt>, then you
-
# will need to add the attribute writer to the allowed list.
-
#
-
# Supported options:
-
# [:allow_destroy]
-
# If true, destroys any members from the attributes hash with a
-
# <tt>_destroy</tt> key and a value that evaluates to +true+
-
# (eg. 1, '1', true, or 'true'). This option is off by default.
-
# [:reject_if]
-
# Allows you to specify a Proc or a Symbol pointing to a method
-
# that checks whether a record should be built for a certain attribute
-
# hash. The hash is passed to the supplied Proc or the method
-
# and it should return either +true+ or +false+. When no :reject_if
-
# is specified, a record will be built for all attribute hashes that
-
# do not have a <tt>_destroy</tt> value that evaluates to true.
-
# Passing <tt>:all_blank</tt> instead of a Proc will create a proc
-
# that will reject a record where all the attributes are blank.
-
# [:limit]
-
# Allows you to specify the maximum number of the associated records that
-
# can be processed with the nested attributes. If the size of the
-
# nested attributes array exceeds the specified limit, NestedAttributes::TooManyRecords
-
# exception is raised. If omitted, any number associations can be processed.
-
# Note that the :limit option is only applicable to one-to-many associations.
-
# [:update_only]
-
# Allows you to specify that an existing record may only be updated.
-
# A new record may only be created when there is no existing record.
-
# This option only works for one-to-one associations and is ignored for
-
# collection associations. This option is off by default.
-
#
-
# Examples:
-
# # creates avatar_attributes=
-
# accepts_nested_attributes_for :avatar, :reject_if => proc { |attributes| attributes['name'].blank? }
-
# # creates avatar_attributes=
-
# accepts_nested_attributes_for :avatar, :reject_if => :all_blank
-
# # creates avatar_attributes= and posts_attributes=
-
# accepts_nested_attributes_for :avatar, :posts, :allow_destroy => true
-
1
def accepts_nested_attributes_for(*attr_names)
-
2
options = { :allow_destroy => false, :update_only => false }
-
2
options.update(attr_names.extract_options!)
-
2
options.assert_valid_keys(:allow_destroy, :reject_if, :limit, :update_only)
-
2
options[:reject_if] = REJECT_ALL_BLANK_PROC if options[:reject_if] == :all_blank
-
-
2
attr_names.each do |association_name|
-
2
if reflection = reflect_on_association(association_name)
-
2
reflection.options[:autosave] = true
-
2
add_autosave_association_callbacks(reflection)
-
-
2
nested_attributes_options = self.nested_attributes_options.dup
-
2
nested_attributes_options[association_name.to_sym] = options
-
2
self.nested_attributes_options = nested_attributes_options
-
-
2
type = (reflection.collection? ? :collection : :one_to_one)
-
-
# def pirate_attributes=(attributes)
-
# assign_nested_attributes_for_one_to_one_association(:pirate, attributes, mass_assignment_options)
-
# end
-
2
class_eval <<-eoruby, __FILE__, __LINE__ + 1
-
if method_defined?(:#{association_name}_attributes=)
-
remove_method(:#{association_name}_attributes=)
-
end
-
def #{association_name}_attributes=(attributes)
-
assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes, mass_assignment_options)
-
end
-
eoruby
-
else
-
raise ArgumentError, "No association found for name `#{association_name}'. Has it been defined yet?"
-
end
-
end
-
end
-
end
-
-
# Returns ActiveRecord::AutosaveAssociation::marked_for_destruction? It's
-
# used in conjunction with fields_for to build a form element for the
-
# destruction of this association.
-
#
-
# See ActionView::Helpers::FormHelper::fields_for for more info.
-
1
def _destroy
-
marked_for_destruction?
-
end
-
-
1
private
-
-
# Attribute hash keys that should not be assigned as normal attributes.
-
# These hash keys are nested attributes implementation details.
-
1
UNASSIGNABLE_KEYS = %w( id _destroy )
-
-
# Assigns the given attributes to the association.
-
#
-
# If update_only is false and the given attributes include an <tt>:id</tt>
-
# that matches the existing record's id, then the existing record will be
-
# modified. If update_only is true, a new record is only created when no
-
# object exists. Otherwise a new record will be built.
-
#
-
# If the given attributes include a matching <tt>:id</tt> attribute, or
-
# update_only is true, and a <tt>:_destroy</tt> key set to a truthy value,
-
# then the existing record will be marked for destruction.
-
1
def assign_nested_attributes_for_one_to_one_association(association_name, attributes, assignment_opts = {})
-
options = self.nested_attributes_options[association_name]
-
attributes = attributes.with_indifferent_access
-
-
if (options[:update_only] || !attributes['id'].blank?) && (record = send(association_name)) &&
-
(options[:update_only] || record.id.to_s == attributes['id'].to_s)
-
assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy], assignment_opts) unless call_reject_if(association_name, attributes)
-
-
elsif attributes['id'].present? && !assignment_opts[:without_protection]
-
raise_nested_attributes_record_not_found(association_name, attributes['id'])
-
-
elsif !reject_new_record?(association_name, attributes)
-
method = "build_#{association_name}"
-
if respond_to?(method)
-
send(method, attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
-
else
-
raise ArgumentError, "Cannot build association #{association_name}. Are you trying to build a polymorphic one-to-one association?"
-
end
-
end
-
end
-
-
# Assigns the given attributes to the collection association.
-
#
-
# Hashes with an <tt>:id</tt> value matching an existing associated record
-
# will update that record. Hashes without an <tt>:id</tt> value will build
-
# a new record for the association. Hashes with a matching <tt>:id</tt>
-
# value and a <tt>:_destroy</tt> key set to a truthy value will mark the
-
# matched record for destruction.
-
#
-
# For example:
-
#
-
# assign_nested_attributes_for_collection_association(:people, {
-
# '1' => { :id => '1', :name => 'Peter' },
-
# '2' => { :name => 'John' },
-
# '3' => { :id => '2', :_destroy => true }
-
# })
-
#
-
# Will update the name of the Person with ID 1, build a new associated
-
# person with the name `John', and mark the associated Person with ID 2
-
# for destruction.
-
#
-
# Also accepts an Array of attribute hashes:
-
#
-
# assign_nested_attributes_for_collection_association(:people, [
-
# { :id => '1', :name => 'Peter' },
-
# { :name => 'John' },
-
# { :id => '2', :_destroy => true }
-
# ])
-
1
def assign_nested_attributes_for_collection_association(association_name, attributes_collection, assignment_opts = {})
-
options = self.nested_attributes_options[association_name]
-
-
unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
-
raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
-
end
-
-
if options[:limit] && attributes_collection.size > options[:limit]
-
raise TooManyRecords, "Maximum #{options[:limit]} records are allowed. Got #{attributes_collection.size} records instead."
-
end
-
-
if attributes_collection.is_a? Hash
-
keys = attributes_collection.keys
-
attributes_collection = if keys.include?('id') || keys.include?(:id)
-
Array.wrap(attributes_collection)
-
else
-
attributes_collection.values
-
end
-
end
-
-
association = association(association_name)
-
-
existing_records = if association.loaded?
-
association.target
-
else
-
attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact
-
attribute_ids.empty? ? [] : association.scoped.where(association.klass.primary_key => attribute_ids)
-
end
-
-
attributes_collection.each do |attributes|
-
attributes = attributes.with_indifferent_access
-
-
if attributes['id'].blank?
-
unless reject_new_record?(association_name, attributes)
-
association.build(attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
-
end
-
elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s }
-
unless association.loaded? || call_reject_if(association_name, attributes)
-
# Make sure we are operating on the actual object which is in the association's
-
# proxy_target array (either by finding it, or adding it if not found)
-
target_record = association.target.detect { |record| record == existing_record }
-
-
if target_record
-
existing_record = target_record
-
else
-
association.add_to_target(existing_record)
-
end
-
-
end
-
-
if !call_reject_if(association_name, attributes)
-
assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy], assignment_opts)
-
end
-
elsif assignment_opts[:without_protection]
-
association.build(attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
-
else
-
raise_nested_attributes_record_not_found(association_name, attributes['id'])
-
end
-
end
-
end
-
-
# Updates a record with the +attributes+ or marks it for destruction if
-
# +allow_destroy+ is +true+ and has_destroy_flag? returns +true+.
-
1
def assign_to_or_mark_for_destruction(record, attributes, allow_destroy, assignment_opts)
-
record.assign_attributes(attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
-
record.mark_for_destruction if has_destroy_flag?(attributes) && allow_destroy
-
end
-
-
# Determines if a hash contains a truthy _destroy key.
-
1
def has_destroy_flag?(hash)
-
ConnectionAdapters::Column.value_to_boolean(hash['_destroy'])
-
end
-
-
# Determines if a new record should be build by checking for
-
# has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
-
# association and evaluates to +true+.
-
1
def reject_new_record?(association_name, attributes)
-
has_destroy_flag?(attributes) || call_reject_if(association_name, attributes)
-
end
-
-
1
def call_reject_if(association_name, attributes)
-
return false if has_destroy_flag?(attributes)
-
case callback = self.nested_attributes_options[association_name][:reject_if]
-
when Symbol
-
method(callback).arity == 0 ? send(callback) : send(callback, attributes)
-
when Proc
-
callback.call(attributes)
-
end
-
end
-
-
1
def raise_nested_attributes_record_not_found(association_name, record_id)
-
raise RecordNotFound, "Couldn't find #{self.class.reflect_on_association(association_name).klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}"
-
end
-
-
1
def unassignable_keys(assignment_opts)
-
assignment_opts[:without_protection] ? UNASSIGNABLE_KEYS - %w[id] : UNASSIGNABLE_KEYS
-
end
-
end
-
end
-
1
require 'active_support/core_ext/class/attribute'
-
-
1
module ActiveRecord
-
# = Active Record Observer
-
#
-
# Observer classes respond to life cycle callbacks to implement trigger-like
-
# behavior outside the original class. This is a great way to reduce the
-
# clutter that normally comes when the model class is burdened with
-
# functionality that doesn't pertain to the core responsibility of the
-
# class. Example:
-
#
-
# class CommentObserver < ActiveRecord::Observer
-
# def after_save(comment)
-
# Notifications.comment("admin@do.com", "New comment was posted", comment).deliver
-
# end
-
# end
-
#
-
# This Observer sends an email when a Comment#save is finished.
-
#
-
# class ContactObserver < ActiveRecord::Observer
-
# def after_create(contact)
-
# contact.logger.info('New contact added!')
-
# end
-
#
-
# def after_destroy(contact)
-
# contact.logger.warn("Contact with an id of #{contact.id} was destroyed!")
-
# end
-
# end
-
#
-
# This Observer uses logger to log when specific callbacks are triggered.
-
#
-
# == Observing a class that can't be inferred
-
#
-
# Observers will by default be mapped to the class with which they share a name. So CommentObserver will
-
# be tied to observing Comment, ProductManagerObserver to ProductManager, and so on. If you want to name your observer
-
# differently than the class you're interested in observing, you can use the Observer.observe class method which takes
-
# either the concrete class (Product) or a symbol for that class (:product):
-
#
-
# class AuditObserver < ActiveRecord::Observer
-
# observe :account
-
#
-
# def after_update(account)
-
# AuditTrail.new(account, "UPDATED")
-
# end
-
# end
-
#
-
# If the audit observer needs to watch more than one kind of object, this can be specified with multiple arguments:
-
#
-
# class AuditObserver < ActiveRecord::Observer
-
# observe :account, :balance
-
#
-
# def after_update(record)
-
# AuditTrail.new(record, "UPDATED")
-
# end
-
# end
-
#
-
# The AuditObserver will now act on both updates to Account and Balance by treating them both as records.
-
#
-
# == Available callback methods
-
#
-
# The observer can implement callback methods for each of the methods described in the Callbacks module.
-
#
-
# == Storing Observers in Rails
-
#
-
# If you're using Active Record within Rails, observer classes are usually stored in app/models with the
-
# naming convention of app/models/audit_observer.rb.
-
#
-
# == Configuration
-
#
-
# In order to activate an observer, list it in the <tt>config.active_record.observers</tt> configuration
-
# setting in your <tt>config/application.rb</tt> file.
-
#
-
# config.active_record.observers = :comment_observer, :signup_observer
-
#
-
# Observers will not be invoked unless you define these in your application configuration.
-
#
-
# == Loading
-
#
-
# Observers register themselves in the model class they observe, since it is the class that
-
# notifies them of events when they occur. As a side-effect, when an observer is loaded its
-
# corresponding model class is loaded.
-
#
-
# Up to (and including) Rails 2.0.2 observers were instantiated between plugins and
-
# application initializers. Now observers are loaded after application initializers,
-
# so observed models can make use of extensions.
-
#
-
# If by any chance you are using observed models in the initialization you can still
-
# load their observers by calling <tt>ModelObserver.instance</tt> before. Observers are
-
# singletons and that call instantiates and registers them.
-
#
-
1
class Observer < ActiveModel::Observer
-
-
1
protected
-
-
1
def observed_classes
-
klasses = super
-
klasses + klasses.map { |klass| klass.descendants }.flatten
-
end
-
-
1
def add_observer!(klass)
-
super
-
define_callbacks klass
-
end
-
-
1
def define_callbacks(klass)
-
observer = self
-
observer_name = observer.class.name.underscore.gsub('/', '__')
-
-
ActiveRecord::Callbacks::CALLBACKS.each do |callback|
-
next unless respond_to?(callback)
-
callback_meth = :"_notify_#{observer_name}_for_#{callback}"
-
unless klass.respond_to?(callback_meth)
-
klass.send(:define_method, callback_meth) do |&block|
-
observer.update(callback, self, &block)
-
end
-
klass.send(callback, callback_meth)
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
# = Active Record Persistence
-
1
module Persistence
-
# Returns true if this object hasn't been saved yet -- that is, a record
-
# for the object doesn't exist in the data store yet; otherwise, returns false.
-
1
def new_record?
-
@new_record
-
end
-
-
# Returns true if this object has been destroyed, otherwise returns false.
-
1
def destroyed?
-
@destroyed
-
end
-
-
# Returns if the record is persisted, i.e. it's not a new record and it was
-
# not destroyed.
-
1
def persisted?
-
!(new_record? || destroyed?)
-
end
-
-
# Saves the model.
-
#
-
# If the model is new a record gets created in the database, otherwise
-
# the existing record gets updated.
-
#
-
# By default, save always run validations. If any of them fail the action
-
# is cancelled and +save+ returns +false+. However, if you supply
-
# :validate => false, validations are bypassed altogether. See
-
# ActiveRecord::Validations for more information.
-
#
-
# There's a series of callbacks associated with +save+. If any of the
-
# <tt>before_*</tt> callbacks return +false+ the action is cancelled and
-
# +save+ returns +false+. See ActiveRecord::Callbacks for further
-
# details.
-
1
def save(*)
-
begin
-
create_or_update
-
rescue ActiveRecord::RecordInvalid
-
false
-
end
-
end
-
-
# Saves the model.
-
#
-
# If the model is new a record gets created in the database, otherwise
-
# the existing record gets updated.
-
#
-
# With <tt>save!</tt> validations always run. If any of them fail
-
# ActiveRecord::RecordInvalid gets raised. See ActiveRecord::Validations
-
# for more information.
-
#
-
# There's a series of callbacks associated with <tt>save!</tt>. If any of
-
# the <tt>before_*</tt> callbacks return +false+ the action is cancelled
-
# and <tt>save!</tt> raises ActiveRecord::RecordNotSaved. See
-
# ActiveRecord::Callbacks for further details.
-
1
def save!(*)
-
create_or_update || raise(RecordNotSaved)
-
end
-
-
# Deletes the record in the database and freezes this instance to
-
# reflect that no changes should be made (since they can't be
-
# persisted). Returns the frozen instance.
-
#
-
# The row is simply removed with an SQL +DELETE+ statement on the
-
# record's primary key, and no callbacks are executed.
-
#
-
# To enforce the object's +before_destroy+ and +after_destroy+
-
# callbacks, Observer methods, or any <tt>:dependent</tt> association
-
# options, use <tt>#destroy</tt>.
-
1
def delete
-
if persisted?
-
self.class.delete(id)
-
IdentityMap.remove(self) if IdentityMap.enabled?
-
end
-
@destroyed = true
-
freeze
-
end
-
-
# Deletes the record in the database and freezes this instance to reflect
-
# that no changes should be made (since they can't be persisted).
-
1
def destroy
-
destroy_associations
-
-
if persisted?
-
IdentityMap.remove(self) if IdentityMap.enabled?
-
pk = self.class.primary_key
-
column = self.class.columns_hash[pk]
-
substitute = connection.substitute_at(column, 0)
-
-
relation = self.class.unscoped.where(
-
self.class.arel_table[pk].eq(substitute))
-
-
relation.bind_values = [[column, id]]
-
relation.delete_all
-
end
-
-
@destroyed = true
-
freeze
-
end
-
-
# Returns an instance of the specified +klass+ with the attributes of the
-
# current record. This is mostly useful in relation to single-table
-
# inheritance structures where you want a subclass to appear as the
-
# superclass. This can be used along with record identification in
-
# Action Pack to allow, say, <tt>Client < Company</tt> to do something
-
# like render <tt>:partial => @client.becomes(Company)</tt> to render that
-
# instance using the companies/company partial instead of clients/client.
-
#
-
# Note: The new instance will share a link to the same attributes as the original class.
-
# So any change to the attributes in either instance will affect the other.
-
1
def becomes(klass)
-
became = klass.new
-
became.instance_variable_set("@attributes", @attributes)
-
became.instance_variable_set("@attributes_cache", @attributes_cache)
-
became.instance_variable_set("@new_record", new_record?)
-
became.instance_variable_set("@destroyed", destroyed?)
-
became.type = klass.name unless self.class.descends_from_active_record?
-
became
-
end
-
-
# Updates a single attribute and saves the record.
-
# This is especially useful for boolean flags on existing records. Also note that
-
#
-
# * Validation is skipped.
-
# * Callbacks are invoked.
-
# * updated_at/updated_on column is updated if that column is available.
-
# * Updates all the attributes that are dirty in this object.
-
#
-
1
def update_attribute(name, value)
-
name = name.to_s
-
raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
-
send("#{name}=", value)
-
save(:validate => false)
-
end
-
-
# Updates a single attribute of an object, without calling save.
-
#
-
# * Validation is skipped.
-
# * Callbacks are skipped.
-
# * updated_at/updated_on column is not updated if that column is available.
-
#
-
1
def update_column(name, value)
-
name = name.to_s
-
raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
-
raise ActiveRecordError, "can not update on a new record object" unless persisted?
-
raw_write_attribute(name, value)
-
self.class.update_all({ name => value }, self.class.primary_key => id) == 1
-
end
-
-
# Updates the attributes of the model from the passed-in hash and saves the
-
# record, all wrapped in a transaction. If the object is invalid, the saving
-
# will fail and false will be returned.
-
#
-
# When updating model attributes, mass-assignment security protection is respected.
-
# If no +:as+ option is supplied then the +:default+ role will be used.
-
# If you want to bypass the protection given by +attr_protected+ and
-
# +attr_accessible+ then you can do so using the +:without_protection+ option.
-
1
def update_attributes(attributes, options = {})
-
# The following transaction covers any possible database side-effects of the
-
# attributes assignment. For example, setting the IDs of a child collection.
-
with_transaction_returning_status do
-
self.assign_attributes(attributes, options)
-
save
-
end
-
end
-
-
# Updates its receiver just like +update_attributes+ but calls <tt>save!</tt> instead
-
# of +save+, so an exception is raised if the record is invalid.
-
1
def update_attributes!(attributes, options = {})
-
# The following transaction covers any possible database side-effects of the
-
# attributes assignment. For example, setting the IDs of a child collection.
-
with_transaction_returning_status do
-
self.assign_attributes(attributes, options)
-
save!
-
end
-
end
-
-
# Initializes +attribute+ to zero if +nil+ and adds the value passed as +by+ (default is 1).
-
# The increment is performed directly on the underlying attribute, no setter is invoked.
-
# Only makes sense for number-based attributes. Returns +self+.
-
1
def increment(attribute, by = 1)
-
self[attribute] ||= 0
-
self[attribute] += by
-
self
-
end
-
-
# Wrapper around +increment+ that saves the record. This method differs from
-
# its non-bang version in that it passes through the attribute setter.
-
# Saving is not subjected to validation checks. Returns +true+ if the
-
# record could be saved.
-
1
def increment!(attribute, by = 1)
-
increment(attribute, by).update_attribute(attribute, self[attribute])
-
end
-
-
# Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1).
-
# The decrement is performed directly on the underlying attribute, no setter is invoked.
-
# Only makes sense for number-based attributes. Returns +self+.
-
1
def decrement(attribute, by = 1)
-
self[attribute] ||= 0
-
self[attribute] -= by
-
self
-
end
-
-
# Wrapper around +decrement+ that saves the record. This method differs from
-
# its non-bang version in that it passes through the attribute setter.
-
# Saving is not subjected to validation checks. Returns +true+ if the
-
# record could be saved.
-
1
def decrement!(attribute, by = 1)
-
decrement(attribute, by).update_attribute(attribute, self[attribute])
-
end
-
-
# Assigns to +attribute+ the boolean opposite of <tt>attribute?</tt>. So
-
# if the predicate returns +true+ the attribute will become +false+. This
-
# method toggles directly the underlying value without calling any setter.
-
# Returns +self+.
-
1
def toggle(attribute)
-
self[attribute] = !send("#{attribute}?")
-
self
-
end
-
-
# Wrapper around +toggle+ that saves the record. This method differs from
-
# its non-bang version in that it passes through the attribute setter.
-
# Saving is not subjected to validation checks. Returns +true+ if the
-
# record could be saved.
-
1
def toggle!(attribute)
-
toggle(attribute).update_attribute(attribute, self[attribute])
-
end
-
-
# Reloads the attributes of this object from the database.
-
# The optional options argument is passed to find when reloading so you
-
# may do e.g. record.reload(:lock => true) to reload the same record with
-
# an exclusive row lock.
-
1
def reload(options = nil)
-
clear_aggregation_cache
-
clear_association_cache
-
-
IdentityMap.without do
-
fresh_object = self.class.unscoped { self.class.find(self.id, options) }
-
@attributes.update(fresh_object.instance_variable_get('@attributes'))
-
end
-
-
@attributes_cache = {}
-
self
-
end
-
-
# Saves the record with the updated_at/on attributes set to the current time.
-
# Please note that no validation is performed and no callbacks are executed.
-
# If an attribute name is passed, that attribute is updated along with
-
# updated_at/on attributes.
-
#
-
# product.touch # updates updated_at/on
-
# product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
-
#
-
# If used along with +belongs_to+ then +touch+ will invoke +touch+ method on associated object.
-
#
-
# class Brake < ActiveRecord::Base
-
# belongs_to :car, :touch => true
-
# end
-
#
-
# class Car < ActiveRecord::Base
-
# belongs_to :corporation, :touch => true
-
# end
-
#
-
# # triggers @brake.car.touch and @brake.car.corporation.touch
-
# @brake.touch
-
1
def touch(name = nil)
-
attributes = timestamp_attributes_for_update_in_model
-
attributes << name if name
-
-
unless attributes.empty?
-
current_time = current_time_from_proper_timezone
-
changes = {}
-
-
attributes.each do |column|
-
changes[column.to_s] = write_attribute(column.to_s, current_time)
-
end
-
-
changes[self.class.locking_column] = increment_lock if locking_enabled?
-
-
@changed_attributes.except!(*changes.keys)
-
primary_key = self.class.primary_key
-
self.class.unscoped.update_all(changes, { primary_key => self[primary_key] }) == 1
-
end
-
end
-
-
1
private
-
-
# A hook to be overridden by association modules.
-
1
def destroy_associations
-
end
-
-
1
def create_or_update
-
raise ReadOnlyRecord if readonly?
-
result = new_record? ? create : update
-
result != false
-
end
-
-
# Updates the associated record with values matching those of the instance attributes.
-
# Returns the number of affected rows.
-
1
def update(attribute_names = @attributes.keys)
-
attributes_with_values = arel_attributes_values(false, false, attribute_names)
-
return 0 if attributes_with_values.empty?
-
klass = self.class
-
stmt = klass.unscoped.where(klass.arel_table[klass.primary_key].eq(id)).arel.compile_update(attributes_with_values)
-
klass.connection.update stmt
-
end
-
-
# Creates a record with values matching those of the instance attributes
-
# and returns its id.
-
1
def create
-
attributes_values = arel_attributes_values(!id.nil?)
-
-
new_id = self.class.unscoped.insert attributes_values
-
-
self.id ||= new_id
-
-
IdentityMap.add(self) if IdentityMap.enabled?
-
@new_record = false
-
id
-
end
-
-
# Initializes the attributes array with keys matching the columns from the linked table and
-
# the values matching the corresponding default value of that column, so
-
# that a new instance, or one populated from a passed-in Hash, still has all the attributes
-
# that instances loaded from the database would.
-
1
def attributes_from_column_definition
-
3
self.class.column_defaults.dup
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/blank'
-
-
1
module ActiveRecord
-
# = Active Record Query Cache
-
1
class QueryCache
-
1
module ClassMethods
-
# Enable the query cache within the block if Active Record is configured.
-
1
def cache(&block)
-
if ActiveRecord::Base.configurations.blank?
-
yield
-
else
-
connection.cache(&block)
-
end
-
end
-
-
# Disable the query cache within the block if Active Record is configured.
-
1
def uncached(&block)
-
if ActiveRecord::Base.configurations.blank?
-
yield
-
else
-
connection.uncached(&block)
-
end
-
end
-
end
-
-
1
def initialize(app)
-
1
@app = app
-
end
-
-
1
class BodyProxy # :nodoc:
-
1
def initialize(original_cache_value, target)
-
@original_cache_value = original_cache_value
-
@target = target
-
end
-
-
1
def method_missing(method_sym, *arguments, &block)
-
@target.send(method_sym, *arguments, &block)
-
end
-
-
1
def respond_to?(method_sym, include_private = false)
-
super || @target.respond_to?(method_sym)
-
end
-
-
1
def each(&block)
-
@target.each(&block)
-
end
-
-
1
def close
-
@target.close if @target.respond_to?(:close)
-
ensure
-
ActiveRecord::Base.connection.clear_query_cache
-
unless @original_cache_value
-
ActiveRecord::Base.connection.disable_query_cache!
-
end
-
end
-
end
-
-
1
def call(env)
-
old = ActiveRecord::Base.connection.query_cache_enabled
-
ActiveRecord::Base.connection.enable_query_cache!
-
-
status, headers, body = @app.call(env)
-
[status, headers, BodyProxy.new(old, body)]
-
rescue Exception => e
-
ActiveRecord::Base.connection.clear_query_cache
-
unless old
-
ActiveRecord::Base.connection.disable_query_cache!
-
end
-
raise e
-
end
-
end
-
end
-
1
require "active_record"
-
1
require "rails"
-
1
require "active_model/railtie"
-
-
# For now, action_controller must always be present with
-
# rails, so let's make sure that it gets required before
-
# here. This is needed for correctly setting up the middleware.
-
# In the future, this might become an optional require.
-
1
require "action_controller/railtie"
-
-
1
module ActiveRecord
-
# = Active Record Railtie
-
1
class Railtie < Rails::Railtie
-
1
config.active_record = ActiveSupport::OrderedOptions.new
-
-
1
config.app_generators.orm :active_record, :migration => true,
-
:timestamps => true
-
-
1
config.app_middleware.insert_after "::ActionDispatch::Callbacks",
-
"ActiveRecord::QueryCache"
-
-
1
config.app_middleware.insert_after "::ActionDispatch::Callbacks",
-
"ActiveRecord::ConnectionAdapters::ConnectionManagement"
-
-
1
rake_tasks do
-
load "active_record/railties/databases.rake"
-
end
-
-
# When loading console, force ActiveRecord::Base to be loaded
-
# to avoid cross references when loading a constant for the
-
# first time. Also, make it output to STDERR.
-
1
console do |app|
-
require "active_record/railties/console_sandbox" if app.sandbox?
-
ActiveRecord::Base.logger = Logger.new(STDERR)
-
end
-
-
1
initializer "active_record.initialize_timezone" do
-
1
ActiveSupport.on_load(:active_record) do
-
1
self.time_zone_aware_attributes = true
-
1
self.default_timezone = :utc
-
end
-
end
-
-
1
initializer "active_record.logger" do
-
2
ActiveSupport.on_load(:active_record) { self.logger ||= ::Rails.logger }
-
end
-
-
1
initializer "active_record.identity_map" do |app|
-
config.app_middleware.insert_after "::ActionDispatch::Callbacks",
-
1
"ActiveRecord::IdentityMap::Middleware" if config.active_record.delete(:identity_map)
-
end
-
-
1
initializer "active_record.set_configs" do |app|
-
1
ActiveSupport.on_load(:active_record) do
-
1
if app.config.active_record.delete(:whitelist_attributes)
-
attr_accessible(nil)
-
end
-
1
app.config.active_record.each do |k,v|
-
send "#{k}=", v
-
end
-
end
-
end
-
-
# This sets the database configuration from Configuration#database_configuration
-
# and then establishes the connection.
-
1
initializer "active_record.initialize_database" do |app|
-
1
ActiveSupport.on_load(:active_record) do
-
1
self.configurations = app.config.database_configuration
-
1
establish_connection
-
end
-
end
-
-
# Expose database runtime to controller for logging.
-
1
initializer "active_record.log_runtime" do |app|
-
1
require "active_record/railties/controller_runtime"
-
1
ActiveSupport.on_load(:action_controller) do
-
1
include ActiveRecord::Railties::ControllerRuntime
-
end
-
end
-
-
1
initializer "active_record.set_dispatch_hooks", :before => :set_clear_dependencies_hook do |app|
-
1
ActiveSupport.on_load(:active_record) do
-
1
ActionDispatch::Reloader.to_cleanup do
-
ActiveRecord::Base.clear_reloadable_connections!
-
ActiveRecord::Base.clear_cache!
-
end
-
end
-
end
-
-
1
config.after_initialize do
-
1
ActiveSupport.on_load(:active_record) do
-
1
instantiate_observers
-
-
1
ActionDispatch::Reloader.to_prepare do
-
ActiveRecord::Base.instantiate_observers
-
end
-
end
-
end
-
-
1
config.after_initialize do
-
1
container = :"activerecord.attributes"
-
1
lookup = I18n.t(container, :default => {})
-
1
if lookup.is_a?(Hash)
-
1
lookup.each do |key, value|
-
if value.is_a?(Hash) && value.any? { |k,v| v.is_a?(Hash) }
-
$stderr.puts "[DEPRECATION WARNING] Nested I18n namespace lookup under \"#{container}.#{key}\" is no longer supported"
-
end
-
end
-
end
-
-
1
container = :"activerecord.models"
-
1
lookup = I18n.t(container, :default => {})
-
1
if lookup.is_a?(Hash)
-
1
lookup.each do |key, value|
-
if value.is_a?(Hash) && !value.key?(:one)
-
$stderr.puts "[DEPRECATION WARNING] Nested I18n namespace lookup under \"#{container}.#{key}\" is no longer supported"
-
end
-
end
-
end
-
end
-
-
end
-
end
-
1
require 'active_support/core_ext/module/attr_internal'
-
1
require 'active_record/log_subscriber'
-
-
1
module ActiveRecord
-
1
module Railties
-
1
module ControllerRuntime #:nodoc:
-
1
extend ActiveSupport::Concern
-
-
1
protected
-
-
1
attr_internal :db_runtime
-
-
1
def process_action(action, *args)
-
# We also need to reset the runtime before each action
-
# because of queries in middleware or in cases we are streaming
-
# and it won't be cleaned up by the method below.
-
ActiveRecord::LogSubscriber.reset_runtime
-
super
-
end
-
-
1
def cleanup_view_runtime
-
if ActiveRecord::Base.connected?
-
db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime
-
runtime = super
-
db_rt_after_render = ActiveRecord::LogSubscriber.reset_runtime
-
self.db_runtime = db_rt_before_render + db_rt_after_render
-
runtime - db_rt_after_render
-
else
-
super
-
end
-
end
-
-
1
def append_info_to_payload(payload)
-
super
-
payload[:db_runtime] = db_runtime
-
end
-
-
1
module ClassMethods
-
1
def log_process_action(payload)
-
messages, db_runtime = super, payload[:db_runtime]
-
messages << ("ActiveRecord: %.1fms" % db_runtime.to_f) if db_runtime
-
messages
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/module/deprecation'
-
1
require 'active_support/core_ext/object/inclusion'
-
-
1
module ActiveRecord
-
# = Active Record Reflection
-
1
module Reflection # :nodoc:
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
class_attribute :reflections
-
1
self.reflections = {}
-
end
-
-
# Reflection enables to interrogate Active Record classes and objects
-
# about their associations and aggregations. This information can,
-
# for example, be used in a form builder that takes an Active Record object
-
# and creates input fields for all of the attributes depending on their type
-
# and displays the associations to other objects.
-
#
-
# MacroReflection class has info for AggregateReflection and AssociationReflection
-
# classes.
-
1
module ClassMethods
-
1
def create_reflection(macro, name, options, active_record)
-
5
case macro
-
when :has_many, :belongs_to, :has_one, :has_and_belongs_to_many
-
5
klass = options[:through] ? ThroughReflection : AssociationReflection
-
5
reflection = klass.new(macro, name, options, active_record)
-
when :composed_of
-
reflection = AggregateReflection.new(macro, name, options, active_record)
-
end
-
-
5
self.reflections = self.reflections.merge(name => reflection)
-
5
reflection
-
end
-
-
# Returns an array of AggregateReflection objects for all the aggregations in the class.
-
1
def reflect_on_all_aggregations
-
reflections.values.grep(AggregateReflection)
-
end
-
-
# Returns the AggregateReflection object for the named +aggregation+ (use the symbol).
-
#
-
# Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
-
#
-
1
def reflect_on_aggregation(aggregation)
-
reflections[aggregation].is_a?(AggregateReflection) ? reflections[aggregation] : nil
-
end
-
-
# Returns an array of AssociationReflection objects for all the
-
# associations in the class. If you only want to reflect on a certain
-
# association type, pass in the symbol (<tt>:has_many</tt>, <tt>:has_one</tt>,
-
# <tt>:belongs_to</tt>) as the first parameter.
-
#
-
# Example:
-
#
-
# Account.reflect_on_all_associations # returns an array of all associations
-
# Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
-
#
-
1
def reflect_on_all_associations(macro = nil)
-
association_reflections = reflections.values.grep(AssociationReflection)
-
macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
-
end
-
-
# Returns the AssociationReflection object for the +association+ (use the symbol).
-
#
-
# Account.reflect_on_association(:owner) # returns the owner AssociationReflection
-
# Invoice.reflect_on_association(:line_items).macro # returns :has_many
-
#
-
1
def reflect_on_association(association)
-
2
reflections[association].is_a?(AssociationReflection) ? reflections[association] : nil
-
end
-
-
# Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
-
1
def reflect_on_all_autosave_associations
-
reflections.values.select { |reflection| reflection.options[:autosave] }
-
end
-
end
-
-
-
# Abstract base class for AggregateReflection and AssociationReflection. Objects of
-
# AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
-
1
class MacroReflection
-
# Returns the name of the macro.
-
#
-
# <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>:balance</tt>
-
# <tt>has_many :clients</tt> returns <tt>:clients</tt>
-
1
attr_reader :name
-
-
# Returns the macro type.
-
#
-
# <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>:composed_of</tt>
-
# <tt>has_many :clients</tt> returns <tt>:has_many</tt>
-
1
attr_reader :macro
-
-
# Returns the hash of options used for the macro.
-
#
-
# <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>{ :class_name => "Money" }</tt>
-
# <tt>has_many :clients</tt> returns +{}+
-
1
attr_reader :options
-
-
1
attr_reader :active_record
-
-
1
attr_reader :plural_name # :nodoc:
-
-
1
def initialize(macro, name, options, active_record)
-
5
@macro = macro
-
5
@name = name
-
5
@options = options
-
5
@active_record = active_record
-
5
@plural_name = active_record.pluralize_table_names ?
-
name.to_s.pluralize : name.to_s
-
end
-
-
# Returns the class for the macro.
-
#
-
# <tt>composed_of :balance, :class_name => 'Money'</tt> returns the Money class
-
# <tt>has_many :clients</tt> returns the Client class
-
1
def klass
-
@klass ||= class_name.constantize
-
end
-
-
# Returns the class name for the macro.
-
#
-
# <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>'Money'</tt>
-
# <tt>has_many :clients</tt> returns <tt>'Client'</tt>
-
1
def class_name
-
@class_name ||= options[:class_name] || derive_class_name
-
end
-
-
# Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
-
# and +other_aggregation+ has an options hash assigned to it.
-
1
def ==(other_aggregation)
-
super ||
-
other_aggregation.kind_of?(self.class) &&
-
name == other_aggregation.name &&
-
other_aggregation.options &&
-
active_record == other_aggregation.active_record
-
end
-
-
1
def sanitized_conditions #:nodoc:
-
@sanitized_conditions ||= klass.send(:sanitize_sql, options[:conditions]) if options[:conditions]
-
end
-
-
1
private
-
1
def derive_class_name
-
name.to_s.camelize
-
end
-
end
-
-
-
# Holds all the meta-data about an aggregation as it was specified in the
-
# Active Record class.
-
1
class AggregateReflection < MacroReflection #:nodoc:
-
end
-
-
# Holds all the meta-data about an association as it was specified in the
-
# Active Record class.
-
1
class AssociationReflection < MacroReflection #:nodoc:
-
# Returns the target association's class.
-
#
-
# class Author < ActiveRecord::Base
-
# has_many :books
-
# end
-
#
-
# Author.reflect_on_association(:books).klass
-
# # => Book
-
#
-
# <b>Note:</b> Do not call +klass.new+ or +klass.create+ to instantiate
-
# a new association object. Use +build_association+ or +create_association+
-
# instead. This allows plugins to hook into association object creation.
-
1
def klass
-
@klass ||= active_record.send(:compute_type, class_name)
-
end
-
-
1
def initialize(macro, name, options, active_record)
-
5
super
-
5
@collection = macro.in?([:has_many, :has_and_belongs_to_many])
-
end
-
-
# This is a hack so that we can tell if build_association was overridden, in order to
-
# provide an appropriate deprecation if the overridden method ignored the &block. Please
-
# see Association#build_record for details.
-
1
attr_accessor :original_build_association_called # :nodoc
-
-
# Returns a new, unsaved instance of the associated class. +options+ will
-
# be passed to the class's constructor.
-
1
def build_association(*options, &block)
-
@original_build_association_called = true
-
klass.new(*options, &block)
-
end
-
-
1
def table_name
-
@table_name ||= klass.table_name
-
end
-
-
1
def quoted_table_name
-
@quoted_table_name ||= klass.quoted_table_name
-
end
-
-
1
def foreign_key
-
@foreign_key ||= options[:foreign_key] || derive_foreign_key
-
end
-
-
1
def primary_key_name
-
foreign_key
-
end
-
1
deprecate :primary_key_name => :foreign_key
-
-
1
def foreign_type
-
@foreign_type ||= options[:foreign_type] || "#{name}_type"
-
end
-
-
1
def type
-
@type ||= options[:as] && "#{options[:as]}_type"
-
end
-
-
1
def primary_key_column
-
@primary_key_column ||= klass.columns.find { |c| c.name == klass.primary_key }
-
end
-
-
1
def association_foreign_key
-
@association_foreign_key ||= options[:association_foreign_key] || class_name.foreign_key
-
end
-
-
1
def association_primary_key
-
@association_primary_key ||=
-
options[:primary_key] ||
-
!options[:polymorphic] && klass.primary_key ||
-
'id'
-
end
-
-
1
def active_record_primary_key
-
@active_record_primary_key ||= options[:primary_key] || active_record.primary_key
-
end
-
-
1
def counter_cache_column
-
if options[:counter_cache] == true
-
"#{active_record.name.demodulize.underscore.pluralize}_count"
-
elsif options[:counter_cache]
-
options[:counter_cache]
-
end
-
end
-
-
1
def columns(tbl_name, log_msg)
-
@columns ||= klass.connection.columns(tbl_name, log_msg)
-
end
-
-
1
def reset_column_information
-
@columns = nil
-
end
-
-
1
def check_validity!
-
check_validity_of_inverse!
-
end
-
-
1
def check_validity_of_inverse!
-
unless options[:polymorphic]
-
if has_inverse? && inverse_of.nil?
-
raise InverseOfAssociationNotFoundError.new(self)
-
end
-
end
-
end
-
-
1
def through_reflection
-
nil
-
end
-
-
1
def source_reflection
-
nil
-
end
-
-
# A chain of reflections from this one back to the owner. For more see the explanation in
-
# ThroughReflection.
-
1
def chain
-
[self]
-
end
-
-
# An array of arrays of conditions. Each item in the outside array corresponds to a reflection
-
# in the #chain. The inside arrays are simply conditions (and each condition may itself be
-
# a hash, array, arel predicate, etc...)
-
1
def conditions
-
[[options[:conditions]].compact]
-
end
-
-
1
alias :source_macro :macro
-
-
1
def has_inverse?
-
@options[:inverse_of]
-
end
-
-
1
def inverse_of
-
if has_inverse?
-
@inverse_of ||= klass.reflect_on_association(options[:inverse_of])
-
end
-
end
-
-
1
def polymorphic_inverse_of(associated_class)
-
if has_inverse?
-
if inverse_relationship = associated_class.reflect_on_association(options[:inverse_of])
-
inverse_relationship
-
else
-
raise InverseOfAssociationNotFoundError.new(self, associated_class)
-
end
-
end
-
end
-
-
# Returns whether or not this association reflection is for a collection
-
# association. Returns +true+ if the +macro+ is either +has_many+ or
-
# +has_and_belongs_to_many+, +false+ otherwise.
-
1
def collection?
-
9
@collection
-
end
-
-
# Returns whether or not the association should be validated as part of
-
# the parent's validation.
-
#
-
# Unless you explicitly disable validation with
-
# <tt>:validate => false</tt>, validation will take place when:
-
#
-
# * you explicitly enable validation; <tt>:validate => true</tt>
-
# * you use autosave; <tt>:autosave => true</tt>
-
# * the association is a +has_many+ association
-
1
def validate?
-
7
!options[:validate].nil? ? options[:validate] : (options[:autosave] == true || macro == :has_many)
-
end
-
-
# Returns +true+ if +self+ is a +belongs_to+ reflection.
-
1
def belongs_to?
-
macro == :belongs_to
-
end
-
-
1
def association_class
-
case macro
-
when :belongs_to
-
if options[:polymorphic]
-
Associations::BelongsToPolymorphicAssociation
-
else
-
Associations::BelongsToAssociation
-
end
-
when :has_and_belongs_to_many
-
Associations::HasAndBelongsToManyAssociation
-
when :has_many
-
if options[:through]
-
Associations::HasManyThroughAssociation
-
else
-
Associations::HasManyAssociation
-
end
-
when :has_one
-
if options[:through]
-
Associations::HasOneThroughAssociation
-
else
-
Associations::HasOneAssociation
-
end
-
end
-
end
-
-
1
private
-
1
def derive_class_name
-
class_name = name.to_s.camelize
-
class_name = class_name.singularize if collection?
-
class_name
-
end
-
-
1
def derive_foreign_key
-
if belongs_to?
-
"#{name}_id"
-
elsif options[:as]
-
"#{options[:as]}_id"
-
else
-
active_record.name.foreign_key
-
end
-
end
-
end
-
-
# Holds all the meta-data about a :through association as it was specified
-
# in the Active Record class.
-
1
class ThroughReflection < AssociationReflection #:nodoc:
-
1
delegate :foreign_key, :foreign_type, :association_foreign_key,
-
:active_record_primary_key, :type, :to => :source_reflection
-
-
# Gets the source of the through reflection. It checks both a singularized
-
# and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
-
#
-
# class Post < ActiveRecord::Base
-
# has_many :taggings
-
# has_many :tags, :through => :taggings
-
# end
-
#
-
1
def source_reflection
-
@source_reflection ||= source_reflection_names.collect { |name| through_reflection.klass.reflect_on_association(name) }.compact.first
-
end
-
-
# Returns the AssociationReflection object specified in the <tt>:through</tt> option
-
# of a HasManyThrough or HasOneThrough association.
-
#
-
# class Post < ActiveRecord::Base
-
# has_many :taggings
-
# has_many :tags, :through => :taggings
-
# end
-
#
-
# tags_reflection = Post.reflect_on_association(:tags)
-
# taggings_reflection = tags_reflection.through_reflection
-
#
-
1
def through_reflection
-
@through_reflection ||= active_record.reflect_on_association(options[:through])
-
end
-
-
# Returns an array of reflections which are involved in this association. Each item in the
-
# array corresponds to a table which will be part of the query for this association.
-
#
-
# The chain is built by recursively calling #chain on the source reflection and the through
-
# reflection. The base case for the recursion is a normal association, which just returns
-
# [self] as its #chain.
-
1
def chain
-
@chain ||= begin
-
chain = source_reflection.chain + through_reflection.chain
-
chain[0] = self # Use self so we don't lose the information from :source_type
-
chain
-
end
-
end
-
-
# Consider the following example:
-
#
-
# class Person
-
# has_many :articles
-
# has_many :comment_tags, :through => :articles
-
# end
-
#
-
# class Article
-
# has_many :comments
-
# has_many :comment_tags, :through => :comments, :source => :tags
-
# end
-
#
-
# class Comment
-
# has_many :tags
-
# end
-
#
-
# There may be conditions on Person.comment_tags, Article.comment_tags and/or Comment.tags,
-
# but only Comment.tags will be represented in the #chain. So this method creates an array
-
# of conditions corresponding to the chain. Each item in the #conditions array corresponds
-
# to an item in the #chain, and is itself an array of conditions from an arbitrary number
-
# of relevant reflections, plus any :source_type or polymorphic :as constraints.
-
1
def conditions
-
@conditions ||= begin
-
conditions = source_reflection.conditions
-
-
# Add to it the conditions from this reflection if necessary.
-
conditions.first << options[:conditions] if options[:conditions]
-
-
through_conditions = through_reflection.conditions
-
-
if options[:source_type]
-
through_conditions.first << { foreign_type => options[:source_type] }
-
end
-
-
# Recursively fill out the rest of the array from the through reflection
-
conditions += through_conditions
-
-
# And return
-
conditions
-
end
-
end
-
-
# The macro used by the source association
-
1
def source_macro
-
source_reflection.source_macro
-
end
-
-
# A through association is nested iff there would be more than one join table
-
1
def nested?
-
chain.length > 2 || through_reflection.macro == :has_and_belongs_to_many
-
end
-
-
# We want to use the klass from this reflection, rather than just delegate straight to
-
# the source_reflection, because the source_reflection may be polymorphic. We still
-
# need to respect the source_reflection's :primary_key option, though.
-
1
def association_primary_key
-
@association_primary_key ||= begin
-
# Get the "actual" source reflection if the immediate source reflection has a
-
# source reflection itself
-
source_reflection = self.source_reflection
-
while source_reflection.source_reflection
-
source_reflection = source_reflection.source_reflection
-
end
-
-
source_reflection.options[:primary_key] || klass.primary_key
-
end
-
end
-
-
# Gets an array of possible <tt>:through</tt> source reflection names:
-
#
-
# [:singularized, :pluralized]
-
#
-
1
def source_reflection_names
-
@source_reflection_names ||= (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }
-
end
-
-
1
def source_options
-
source_reflection.options
-
end
-
-
1
def through_options
-
through_reflection.options
-
end
-
-
1
def check_validity!
-
if through_reflection.nil?
-
raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
-
end
-
-
if through_reflection.options[:polymorphic]
-
raise HasManyThroughAssociationPolymorphicThroughError.new(active_record.name, self)
-
end
-
-
if source_reflection.nil?
-
raise HasManyThroughSourceAssociationNotFoundError.new(self)
-
end
-
-
if options[:source_type] && source_reflection.options[:polymorphic].nil?
-
raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection)
-
end
-
-
if source_reflection.options[:polymorphic] && options[:source_type].nil?
-
raise HasManyThroughAssociationPolymorphicSourceError.new(active_record.name, self, source_reflection)
-
end
-
-
if macro == :has_one && through_reflection.collection?
-
raise HasOneThroughCantAssociateThroughCollection.new(active_record.name, self, through_reflection)
-
end
-
-
check_validity_of_inverse!
-
end
-
-
1
private
-
1
def derive_class_name
-
# get the class_name of the belongs_to association of the through reflection
-
options[:source_type] || source_reflection.class_name
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/module/delegation'
-
-
1
module ActiveRecord
-
# = Active Record Relation
-
1
class Relation
-
1
JoinOperation = Struct.new(:relation, :join_class, :on)
-
1
ASSOCIATION_METHODS = [:includes, :eager_load, :preload]
-
1
MULTI_VALUE_METHODS = [:select, :group, :order, :joins, :where, :having, :bind]
-
1
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reorder, :reverse_order]
-
-
1
include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches
-
-
# These are explicitly delegated to improve performance (avoids method_missing)
-
1
delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to => :to_a
-
1
delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key, :to => :klass
-
-
1
attr_reader :table, :klass, :loaded
-
1
attr_accessor :extensions, :default_scoped
-
1
alias :loaded? :loaded
-
1
alias :default_scoped? :default_scoped
-
-
1
def initialize(klass, table)
-
2
@klass, @table = klass, table
-
-
2
@implicit_readonly = nil
-
2
@loaded = false
-
2
@default_scoped = false
-
-
16
SINGLE_VALUE_METHODS.each {|v| instance_variable_set(:"@#{v}_value", nil)}
-
22
(ASSOCIATION_METHODS + MULTI_VALUE_METHODS).each {|v| instance_variable_set(:"@#{v}_values", [])}
-
2
@extensions = []
-
2
@create_with_value = {}
-
end
-
-
1
def insert(values)
-
primary_key_value = nil
-
-
if primary_key && Hash === values
-
primary_key_value = values[values.keys.find { |k|
-
k.name == primary_key
-
}]
-
-
if !primary_key_value && connection.prefetch_primary_key?(klass.table_name)
-
primary_key_value = connection.next_sequence_value(klass.sequence_name)
-
values[klass.arel_table[klass.primary_key]] = primary_key_value
-
end
-
end
-
-
im = arel.create_insert
-
im.into @table
-
-
conn = @klass.connection
-
-
substitutes = values.sort_by { |arel_attr,_| arel_attr.name }
-
binds = substitutes.map do |arel_attr, value|
-
[@klass.columns_hash[arel_attr.name], value]
-
end
-
-
substitutes.each_with_index do |tuple, i|
-
tuple[1] = conn.substitute_at(binds[i][0], i)
-
end
-
-
if values.empty? # empty insert
-
im.values = Arel.sql(connection.empty_insert_statement_value)
-
else
-
im.insert substitutes
-
end
-
-
conn.insert(
-
im,
-
'SQL',
-
primary_key,
-
primary_key_value,
-
nil,
-
binds)
-
end
-
-
1
def new(*args, &block)
-
scoping { @klass.new(*args, &block) }
-
end
-
-
1
def initialize_copy(other)
-
reset
-
end
-
-
1
alias build new
-
-
1
def create(*args, &block)
-
scoping { @klass.create(*args, &block) }
-
end
-
-
1
def create!(*args, &block)
-
scoping { @klass.create!(*args, &block) }
-
end
-
-
1
def respond_to?(method, include_private = false)
-
arel.respond_to?(method, include_private) ||
-
Array.method_defined?(method) ||
-
@klass.respond_to?(method, include_private) ||
-
super
-
end
-
-
1
def to_a
-
return @records if loaded?
-
-
default_scoped = with_default_scope
-
-
if default_scoped.equal?(self)
-
@records = if @readonly_value.nil? && !@klass.locking_enabled?
-
eager_loading? ? find_with_associations : @klass.find_by_sql(arel, @bind_values)
-
else
-
IdentityMap.without do
-
eager_loading? ? find_with_associations : @klass.find_by_sql(arel, @bind_values)
-
end
-
end
-
-
preload = @preload_values
-
preload += @includes_values unless eager_loading?
-
preload.each do |associations|
-
ActiveRecord::Associations::Preloader.new(@records, associations).run
-
end
-
-
# @readonly_value is true only if set explicitly. @implicit_readonly is true if there
-
# are JOINS and no explicit SELECT.
-
readonly = @readonly_value.nil? ? @implicit_readonly : @readonly_value
-
@records.each { |record| record.readonly! } if readonly
-
else
-
@records = default_scoped.to_a
-
end
-
-
@loaded = true
-
@records
-
end
-
-
1
def as_json(options = nil) #:nodoc:
-
to_a.as_json(options)
-
end
-
-
# Returns size of the records.
-
1
def size
-
loaded? ? @records.length : count
-
end
-
-
# Returns true if there are no records.
-
1
def empty?
-
return @records.empty? if loaded?
-
-
c = count
-
c.respond_to?(:zero?) ? c.zero? : c.empty?
-
end
-
-
1
def any?
-
if block_given?
-
to_a.any? { |*block_args| yield(*block_args) }
-
else
-
!empty?
-
end
-
end
-
-
1
def many?
-
if block_given?
-
to_a.many? { |*block_args| yield(*block_args) }
-
else
-
@limit_value ? to_a.many? : size > 1
-
end
-
end
-
-
# Scope all queries to the current scope.
-
#
-
# ==== Example
-
#
-
# Comment.where(:post_id => 1).scoping do
-
# Comment.first # SELECT * FROM comments WHERE post_id = 1
-
# end
-
#
-
# Please check unscoped if you want to remove all previous scopes (including
-
# the default_scope) during the execution of a block.
-
1
def scoping
-
@klass.send(:with_scope, self, :overwrite) { yield }
-
end
-
-
# Updates all records with details given if they match a set of conditions supplied, limits and order can
-
# also be supplied. This method constructs a single SQL UPDATE statement and sends it straight to the
-
# database. It does not instantiate the involved models and it does not trigger Active Record callbacks
-
# or validations.
-
#
-
# ==== Parameters
-
#
-
# * +updates+ - A string, array, or hash representing the SET part of an SQL statement.
-
# * +conditions+ - A string, array, or hash representing the WHERE part of an SQL statement.
-
# See conditions in the intro.
-
# * +options+ - Additional options are <tt>:limit</tt> and <tt>:order</tt>, see the examples for usage.
-
#
-
# ==== Examples
-
#
-
# # Update all customers with the given attributes
-
# Customer.update_all :wants_email => true
-
#
-
# # Update all books with 'Rails' in their title
-
# Book.update_all "author = 'David'", "title LIKE '%Rails%'"
-
#
-
# # Update all avatars migrated more than a week ago
-
# Avatar.update_all ['migrated_at = ?', Time.now.utc], ['migrated_at > ?', 1.week.ago]
-
#
-
# # Update all books that match conditions, but limit it to 5 ordered by date
-
# Book.update_all "author = 'David'", "title LIKE '%Rails%'", :order => 'created_at', :limit => 5
-
#
-
# # Conditions from the current relation also works
-
# Book.where('title LIKE ?', '%Rails%').update_all(:author => 'David')
-
#
-
# # The same idea applies to limit and order
-
# Book.where('title LIKE ?', '%Rails%').order(:created_at).limit(5).update_all(:author => 'David')
-
1
def update_all(updates, conditions = nil, options = {})
-
IdentityMap.repository[symbolized_base_class].clear if IdentityMap.enabled?
-
if conditions || options.present?
-
where(conditions).apply_finder_options(options.slice(:limit, :order)).update_all(updates)
-
else
-
stmt = Arel::UpdateManager.new(arel.engine)
-
-
stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates))
-
stmt.table(table)
-
stmt.key = table[primary_key]
-
-
if joins_values.any?
-
@klass.connection.join_to_update(stmt, arel)
-
else
-
stmt.take(arel.limit)
-
stmt.order(*arel.orders)
-
stmt.wheres = arel.constraints
-
end
-
-
@klass.connection.update stmt, 'SQL', bind_values
-
end
-
end
-
-
# Updates an object (or multiple objects) and saves it to the database, if validations pass.
-
# The resulting object is returned whether the object was saved successfully to the database or not.
-
#
-
# ==== Parameters
-
#
-
# * +id+ - This should be the id or an array of ids to be updated.
-
# * +attributes+ - This should be a hash of attributes or an array of hashes.
-
#
-
# ==== Examples
-
#
-
# # Updates one record
-
# Person.update(15, :user_name => 'Samuel', :group => 'expert')
-
#
-
# # Updates multiple records
-
# people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
-
# Person.update(people.keys, people.values)
-
1
def update(id, attributes)
-
if id.is_a?(Array)
-
idx = -1
-
id.collect { |one_id| idx += 1; update(one_id, attributes[idx]) }
-
else
-
object = find(id)
-
object.update_attributes(attributes)
-
object
-
end
-
end
-
-
# Destroys the records matching +conditions+ by instantiating each
-
# record and calling its +destroy+ method. Each object's callbacks are
-
# executed (including <tt>:dependent</tt> association options and
-
# +before_destroy+/+after_destroy+ Observer methods). Returns the
-
# collection of objects that were destroyed; each will be frozen, to
-
# reflect that no changes should be made (since they can't be
-
# persisted).
-
#
-
# Note: Instantiation, callback execution, and deletion of each
-
# record can be time consuming when you're removing many records at
-
# once. It generates at least one SQL +DELETE+ query per record (or
-
# possibly more, to enforce your callbacks). If you want to delete many
-
# rows quickly, without concern for their associations or callbacks, use
-
# +delete_all+ instead.
-
#
-
# ==== Parameters
-
#
-
# * +conditions+ - A string, array, or hash that specifies which records
-
# to destroy. If omitted, all records are destroyed. See the
-
# Conditions section in the introduction to ActiveRecord::Base for
-
# more information.
-
#
-
# ==== Examples
-
#
-
# Person.destroy_all("last_login < '2004-04-04'")
-
# Person.destroy_all(:status => "inactive")
-
# Person.where(:age => 0..18).destroy_all
-
1
def destroy_all(conditions = nil)
-
if conditions
-
where(conditions).destroy_all
-
else
-
to_a.each {|object| object.destroy }.tap { reset }
-
end
-
end
-
-
# Destroy an object (or multiple objects) that has the given id, the object is instantiated first,
-
# therefore all callbacks and filters are fired off before the object is deleted. This method is
-
# less efficient than ActiveRecord#delete but allows cleanup methods and other actions to be run.
-
#
-
# This essentially finds the object (or multiple objects) with the given id, creates a new object
-
# from the attributes, and then calls destroy on it.
-
#
-
# ==== Parameters
-
#
-
# * +id+ - Can be either an Integer or an Array of Integers.
-
#
-
# ==== Examples
-
#
-
# # Destroy a single object
-
# Todo.destroy(1)
-
#
-
# # Destroy multiple objects
-
# todos = [1,2,3]
-
# Todo.destroy(todos)
-
1
def destroy(id)
-
if id.is_a?(Array)
-
id.map { |one_id| destroy(one_id) }
-
else
-
find(id).destroy
-
end
-
end
-
-
# Deletes the records matching +conditions+ without instantiating the records first, and hence not
-
# calling the +destroy+ method nor invoking callbacks. This is a single SQL DELETE statement that
-
# goes straight to the database, much more efficient than +destroy_all+. Be careful with relations
-
# though, in particular <tt>:dependent</tt> rules defined on associations are not honored. Returns
-
# the number of rows affected.
-
#
-
# ==== Parameters
-
#
-
# * +conditions+ - Conditions are specified the same way as with +find+ method.
-
#
-
# ==== Example
-
#
-
# Post.delete_all("person_id = 5 AND (category = 'Something' OR category = 'Else')")
-
# Post.delete_all(["person_id = ? AND (category = ? OR category = ?)", 5, 'Something', 'Else'])
-
# Post.where(:person_id => 5).where(:category => ['Something', 'Else']).delete_all
-
#
-
# Both calls delete the affected posts all at once with a single DELETE statement.
-
# If you need to destroy dependent associations or call your <tt>before_*</tt> or
-
# +after_destroy+ callbacks, use the +destroy_all+ method instead.
-
1
def delete_all(conditions = nil)
-
IdentityMap.repository[symbolized_base_class] = {} if IdentityMap.enabled?
-
if conditions
-
where(conditions).delete_all
-
else
-
statement = arel.compile_delete
-
affected = @klass.connection.delete(statement, 'SQL', bind_values)
-
-
reset
-
affected
-
end
-
end
-
-
# Deletes the row with a primary key matching the +id+ argument, using a
-
# SQL +DELETE+ statement, and returns the number of rows deleted. Active
-
# Record objects are not instantiated, so the object's callbacks are not
-
# executed, including any <tt>:dependent</tt> association options or
-
# Observer methods.
-
#
-
# You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
-
#
-
# Note: Although it is often much faster than the alternative,
-
# <tt>#destroy</tt>, skipping callbacks might bypass business logic in
-
# your application that ensures referential integrity or performs other
-
# essential jobs.
-
#
-
# ==== Examples
-
#
-
# # Delete a single row
-
# Todo.delete(1)
-
#
-
# # Delete multiple rows
-
# Todo.delete([2,3,4])
-
1
def delete(id_or_array)
-
IdentityMap.remove_by_id(self.symbolized_base_class, id_or_array) if IdentityMap.enabled?
-
where(primary_key => id_or_array).delete_all
-
end
-
-
1
def reload
-
reset
-
to_a # force reload
-
self
-
end
-
-
1
def reset
-
@first = @last = @to_sql = @order_clause = @scope_for_create = @arel = @loaded = nil
-
@should_eager_load = @join_dependency = nil
-
@records = []
-
self
-
end
-
-
1
def to_sql
-
@to_sql ||= klass.connection.to_sql(arel)
-
end
-
-
1
def where_values_hash
-
equalities = with_default_scope.where_values.grep(Arel::Nodes::Equality).find_all { |node|
-
node.left.relation.name == table_name
-
}
-
-
Hash[equalities.map { |where| [where.left.name, where.right] }]
-
end
-
-
1
def scope_for_create
-
@scope_for_create ||= where_values_hash.merge(create_with_value)
-
end
-
-
1
def eager_loading?
-
@should_eager_load ||=
-
@eager_load_values.any? ||
-
@includes_values.any? && (joined_includes_values.any? || references_eager_loaded_tables?)
-
end
-
-
# Joins that are also marked for preloading. In which case we should just eager load them.
-
# Note that this is a naive implementation because we could have strings and symbols which
-
# represent the same association, but that aren't matched by this. Also, we could have
-
# nested hashes which partially match, e.g. { :a => :b } & { :a => [:b, :c] }
-
1
def joined_includes_values
-
@includes_values & @joins_values
-
end
-
-
1
def ==(other)
-
case other
-
when Relation
-
other.to_sql == to_sql
-
when Array
-
to_a == other
-
end
-
end
-
-
1
def inspect
-
to_a.inspect
-
end
-
-
1
def with_default_scope #:nodoc:
-
if default_scoped? && default_scope = klass.send(:build_default_scope)
-
default_scope = default_scope.merge(self)
-
default_scope.default_scoped = false
-
default_scope
-
else
-
self
-
end
-
end
-
-
1
protected
-
-
1
def method_missing(method, *args, &block)
-
if Array.method_defined?(method)
-
to_a.send(method, *args, &block)
-
elsif @klass.respond_to?(method)
-
scoping { @klass.send(method, *args, &block) }
-
elsif arel.respond_to?(method)
-
arel.send(method, *args, &block)
-
else
-
super
-
end
-
end
-
-
1
private
-
-
1
def references_eager_loaded_tables?
-
joined_tables = arel.join_sources.map do |join|
-
if join.is_a?(Arel::Nodes::StringJoin)
-
tables_in_string(join.left)
-
else
-
[join.left.table_name, join.left.table_alias]
-
end
-
end
-
-
joined_tables += [table.name, table.table_alias]
-
-
# always convert table names to downcase as in Oracle quoted table names are in uppercase
-
joined_tables = joined_tables.flatten.compact.map { |t| t.downcase }.uniq
-
-
(tables_in_string(to_sql) - joined_tables).any?
-
end
-
-
1
def tables_in_string(string)
-
return [] if string.blank?
-
# always convert table names to downcase as in Oracle quoted table names are in uppercase
-
# ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
-
string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map{ |s| s.downcase }.uniq - ['raw_sql_']
-
end
-
-
end
-
end
-
1
require 'active_support/core_ext/object/blank'
-
-
1
module ActiveRecord
-
1
module Batches
-
# Yields each record that was found by the find +options+. The find is
-
# performed by find_in_batches with a batch size of 1000 (or as
-
# specified by the <tt>:batch_size</tt> option).
-
#
-
# Example:
-
#
-
# Person.where("age > 21").find_each do |person|
-
# person.party_all_night!
-
# end
-
#
-
# Note: This method is only intended to use for batch processing of
-
# large amounts of records that wouldn't fit in memory all at once. If
-
# you just need to loop over less than 1000 records, it's probably
-
# better just to use the regular find methods.
-
1
def find_each(options = {})
-
find_in_batches(options) do |records|
-
records.each { |record| yield record }
-
end
-
end
-
-
# Yields each batch of records that was found by the find +options+ as
-
# an array. The size of each batch is set by the <tt>:batch_size</tt>
-
# option; the default is 1000.
-
#
-
# You can control the starting point for the batch processing by
-
# supplying the <tt>:start</tt> option. This is especially useful if you
-
# want multiple workers dealing with the same processing queue. You can
-
# make worker 1 handle all the records between id 0 and 10,000 and
-
# worker 2 handle from 10,000 and beyond (by setting the <tt>:start</tt>
-
# option on that worker).
-
#
-
# It's not possible to set the order. That is automatically set to
-
# ascending on the primary key ("id ASC") to make the batch ordering
-
# work. This also mean that this method only works with integer-based
-
# primary keys. You can't set the limit either, that's used to control
-
# the batch sizes.
-
#
-
# Example:
-
#
-
# Person.where("age > 21").find_in_batches do |group|
-
# sleep(50) # Make sure it doesn't get too crowded in there!
-
# group.each { |person| person.party_all_night! }
-
# end
-
1
def find_in_batches(options = {})
-
relation = self
-
-
unless arel.orders.blank? && arel.taken.blank?
-
ActiveRecord::Base.logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size")
-
end
-
-
if (finder_options = options.except(:start, :batch_size)).present?
-
raise "You can't specify an order, it's forced to be #{batch_order}" if options[:order].present?
-
raise "You can't specify a limit, it's forced to be the batch_size" if options[:limit].present?
-
-
relation = apply_finder_options(finder_options)
-
end
-
-
start = options.delete(:start).to_i
-
batch_size = options.delete(:batch_size) || 1000
-
-
relation = relation.except(:order).order(batch_order).limit(batch_size)
-
records = relation.where(table[primary_key].gteq(start)).all
-
-
while records.any?
-
yield records
-
-
break if records.size < batch_size
-
-
if primary_key_offset = records.last.id
-
records = relation.where(table[primary_key].gt(primary_key_offset)).to_a
-
else
-
raise "Primary key not included in the custom select clause"
-
end
-
end
-
end
-
-
1
private
-
-
1
def batch_order
-
"#{quoted_table_name}.#{quoted_primary_key} ASC"
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/object/try'
-
-
1
module ActiveRecord
-
1
module Calculations
-
# Count operates using three different approaches.
-
#
-
# * Count all: By not passing any parameters to count, it will return a count of all the rows for the model.
-
# * Count using column: By passing a column name to count, it will return a count of all the
-
# rows for the model with supplied column present.
-
# * Count using options will find the row count matched by the options used.
-
#
-
# The third approach, count using options, accepts an option hash as the only parameter. The options are:
-
#
-
# * <tt>:conditions</tt>: An SQL fragment like "administrator = 1" or [ "user_name = ?", username ].
-
# See conditions in the intro to ActiveRecord::Base.
-
# * <tt>:joins</tt>: Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id"
-
# (rarely needed) or named associations in the same form used for the <tt>:include</tt> option, which will
-
# perform an INNER JOIN on the associated table(s). If the value is a string, then the records
-
# will be returned read-only since they will have attributes that do not correspond to the table's columns.
-
# Pass <tt>:readonly => false</tt> to override.
-
# * <tt>:include</tt>: Named associations that should be loaded alongside using LEFT OUTER JOINs.
-
# The symbols named refer to already defined associations. When using named associations, count
-
# returns the number of DISTINCT items for the model you're counting.
-
# See eager loading under Associations.
-
# * <tt>:order</tt>: An SQL fragment like "created_at DESC, name" (really only used with GROUP BY calculations).
-
# * <tt>:group</tt>: An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
-
# * <tt>:select</tt>: By default, this is * as in SELECT * FROM, but can be changed if you, for example,
-
# want to do a join but not include the joined columns.
-
# * <tt>:distinct</tt>: Set this to true to make this a distinct calculation, such as
-
# SELECT COUNT(DISTINCT posts.id) ...
-
# * <tt>:from</tt> - By default, this is the table name of the class, but can be changed to an
-
# alternate table name (or even the name of a database view).
-
#
-
# Examples for counting all:
-
# Person.count # returns the total count of all people
-
#
-
# Examples for counting by column:
-
# Person.count(:age) # returns the total count of all people whose age is present in database
-
#
-
# Examples for count with options:
-
# Person.count(:conditions => "age > 26")
-
#
-
# # because of the named association, it finds the DISTINCT count using LEFT OUTER JOIN.
-
# Person.count(:conditions => "age > 26 AND job.salary > 60000", :include => :job)
-
#
-
# # finds the number of rows matching the conditions and joins.
-
# Person.count(:conditions => "age > 26 AND job.salary > 60000",
-
# :joins => "LEFT JOIN jobs on jobs.person_id = person.id")
-
#
-
# Person.count('id', :conditions => "age > 26") # Performs a COUNT(id)
-
# Person.count(:all, :conditions => "age > 26") # Performs a COUNT(*) (:all is an alias for '*')
-
#
-
# Note: <tt>Person.count(:all)</tt> will not work because it will use <tt>:all</tt> as the condition.
-
# Use Person.count instead.
-
1
def count(column_name = nil, options = {})
-
column_name, options = nil, column_name if column_name.is_a?(Hash)
-
calculate(:count, column_name, options)
-
end
-
-
# Calculates the average value on a given column. Returns +nil+ if there's
-
# no row. See +calculate+ for examples with options.
-
#
-
# Person.average('age') # => 35.8
-
1
def average(column_name, options = {})
-
calculate(:average, column_name, options)
-
end
-
-
# Calculates the minimum value on a given column. The value is returned
-
# with the same data type of the column, or +nil+ if there's no row. See
-
# +calculate+ for examples with options.
-
#
-
# Person.minimum('age') # => 7
-
1
def minimum(column_name, options = {})
-
calculate(:minimum, column_name, options)
-
end
-
-
# Calculates the maximum value on a given column. The value is returned
-
# with the same data type of the column, or +nil+ if there's no row. See
-
# +calculate+ for examples with options.
-
#
-
# Person.maximum('age') # => 93
-
1
def maximum(column_name, options = {})
-
calculate(:maximum, column_name, options)
-
end
-
-
# Calculates the sum of values on a given column. The value is returned
-
# with the same data type of the column, 0 if there's no row. See
-
# +calculate+ for examples with options.
-
#
-
# Person.sum('age') # => 4562
-
1
def sum(column_name, options = {})
-
calculate(:sum, column_name, options)
-
end
-
-
# This calculates aggregate values in the given column. Methods for count, sum, average,
-
# minimum, and maximum have been added as shortcuts. Options such as <tt>:conditions</tt>,
-
# <tt>:order</tt>, <tt>:group</tt>, <tt>:having</tt>, and <tt>:joins</tt> can be passed to customize the query.
-
#
-
# There are two basic forms of output:
-
# * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float
-
# for AVG, and the given column's type for everything else.
-
# * Grouped values: This returns an ordered hash of the values and groups them by the
-
# <tt>:group</tt> option. It takes either a column name, or the name of a belongs_to association.
-
#
-
# values = Person.maximum(:age, :group => 'last_name')
-
# puts values["Drake"]
-
# => 43
-
#
-
# drake = Family.find_by_last_name('Drake')
-
# values = Person.maximum(:age, :group => :family) # Person belongs_to :family
-
# puts values[drake]
-
# => 43
-
#
-
# values.each do |family, max_age|
-
# ...
-
# end
-
#
-
# Options:
-
# * <tt>:conditions</tt> - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ].
-
# See conditions in the intro to ActiveRecord::Base.
-
# * <tt>:include</tt>: Eager loading, see Associations for details. Since calculations don't load anything,
-
# the purpose of this is to access fields on joined tables in your conditions, order, or group clauses.
-
# * <tt>:joins</tt> - An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id".
-
# (Rarely needed).
-
# The records will be returned read-only since they will have attributes that do not correspond to the
-
# table's columns.
-
# * <tt>:order</tt> - An SQL fragment like "created_at DESC, name" (really only used with GROUP BY calculations).
-
# * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
-
# * <tt>:select</tt> - By default, this is * as in SELECT * FROM, but can be changed if you for example
-
# want to do a join, but not include the joined columns.
-
# * <tt>:distinct</tt> - Set this to true to make this a distinct calculation, such as
-
# SELECT COUNT(DISTINCT posts.id) ...
-
#
-
# Examples:
-
# Person.calculate(:count, :all) # The same as Person.count
-
# Person.average(:age) # SELECT AVG(age) FROM people...
-
# Person.minimum(:age, :conditions => ['last_name != ?', 'Drake']) # Selects the minimum age for
-
# # everyone with a last name other than 'Drake'
-
#
-
# # Selects the minimum age for any family without any minors
-
# Person.minimum(:age, :having => 'min(age) > 17', :group => :last_name)
-
#
-
# Person.sum("2 * age")
-
1
def calculate(operation, column_name, options = {})
-
if options.except(:distinct).present?
-
apply_finder_options(options.except(:distinct)).calculate(operation, column_name, :distinct => options[:distinct])
-
else
-
relation = with_default_scope
-
-
if relation.equal?(self)
-
if eager_loading? || (includes_values.present? && references_eager_loaded_tables?)
-
construct_relation_for_association_calculations.calculate(operation, column_name, options)
-
else
-
perform_calculation(operation, column_name, options)
-
end
-
else
-
relation.calculate(operation, column_name, options)
-
end
-
end
-
rescue ThrowResult
-
0
-
end
-
-
1
private
-
-
1
def perform_calculation(operation, column_name, options = {})
-
operation = operation.to_s.downcase
-
-
distinct = options[:distinct]
-
-
if operation == "count"
-
column_name ||= (select_for_count || :all)
-
-
unless arel.ast.grep(Arel::Nodes::OuterJoin).empty?
-
distinct = true
-
end
-
-
column_name = primary_key if column_name == :all && distinct
-
-
distinct = nil if column_name =~ /\s*DISTINCT\s+/i
-
end
-
-
if @group_values.any?
-
execute_grouped_calculation(operation, column_name, distinct)
-
else
-
execute_simple_calculation(operation, column_name, distinct)
-
end
-
end
-
-
1
def aggregate_column(column_name)
-
if @klass.column_names.include?(column_name.to_s)
-
Arel::Attribute.new(@klass.unscoped.table, column_name)
-
else
-
Arel.sql(column_name == :all ? "*" : column_name.to_s)
-
end
-
end
-
-
1
def operation_over_aggregate_column(column, operation, distinct)
-
operation == 'count' ? column.count(distinct) : column.send(operation)
-
end
-
-
1
def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
-
# Postgresql doesn't like ORDER BY when there are no GROUP BY
-
relation = reorder(nil)
-
-
if operation == "count" && (relation.limit_value || relation.offset_value)
-
# Shortcut when limit is zero.
-
return 0 if relation.limit_value == 0
-
-
query_builder = build_count_subquery(relation, column_name, distinct)
-
else
-
column = aggregate_column(column_name)
-
-
select_value = operation_over_aggregate_column(column, operation, distinct)
-
-
relation.select_values = [select_value]
-
-
query_builder = relation.arel
-
end
-
-
type_cast_calculated_value(@klass.connection.select_value(query_builder), column_for(column_name), operation)
-
end
-
-
1
def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
-
group_attr = @group_values
-
association = @klass.reflect_on_association(group_attr.first.to_sym)
-
associated = group_attr.size == 1 && association && association.macro == :belongs_to # only count belongs_to associations
-
group_fields = Array(associated ? association.foreign_key : group_attr)
-
group_aliases = group_fields.map { |field| column_alias_for(field) }
-
group_columns = group_aliases.zip(group_fields).map { |aliaz,field|
-
[aliaz, column_for(field)]
-
}
-
-
group = @klass.connection.adapter_name == 'FrontBase' ? group_aliases : group_fields
-
-
if operation == 'count' && column_name == :all
-
aggregate_alias = 'count_all'
-
else
-
aggregate_alias = column_alias_for(operation, column_name)
-
end
-
-
select_values = [
-
operation_over_aggregate_column(
-
aggregate_column(column_name),
-
operation,
-
distinct).as(aggregate_alias)
-
]
-
-
select_values.concat group_fields.zip(group_aliases).map { |field,aliaz|
-
"#{field} AS #{aliaz}"
-
}
-
-
relation = except(:group).group(group.join(','))
-
relation.select_values = select_values
-
-
calculated_data = @klass.connection.select_all(relation)
-
-
if association
-
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
-
key_records = association.klass.base_class.find(key_ids)
-
key_records = Hash[key_records.map { |r| [r.id, r] }]
-
end
-
-
ActiveSupport::OrderedHash[calculated_data.map do |row|
-
key = group_columns.map { |aliaz, column|
-
type_cast_calculated_value(row[aliaz], column)
-
}
-
key = key.first if key.size == 1
-
key = key_records[key] if associated
-
[key, type_cast_calculated_value(row[aggregate_alias], column_for(column_name), operation)]
-
end]
-
end
-
-
# Converts the given keys to the value that the database adapter returns as
-
# a usable column name:
-
#
-
# column_alias_for("users.id") # => "users_id"
-
# column_alias_for("sum(id)") # => "sum_id"
-
# column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
-
# column_alias_for("count(*)") # => "count_all"
-
# column_alias_for("count", "id") # => "count_id"
-
1
def column_alias_for(*keys)
-
table_name = keys.join(' ')
-
table_name.downcase!
-
table_name.gsub!(/\*/, 'all')
-
table_name.gsub!(/\W+/, ' ')
-
table_name.strip!
-
table_name.gsub!(/ +/, '_')
-
-
@klass.connection.table_alias_for(table_name)
-
end
-
-
1
def column_for(field)
-
field_name = field.to_s.split('.').last
-
@klass.columns.detect { |c| c.name.to_s == field_name }
-
end
-
-
1
def type_cast_calculated_value(value, column, operation = nil)
-
case operation
-
when 'count' then value.to_i
-
when 'sum' then type_cast_using_column(value || '0', column)
-
when 'average' then value.respond_to?(:to_d) ? value.to_d : value
-
else type_cast_using_column(value, column)
-
end
-
end
-
-
1
def type_cast_using_column(value, column)
-
column ? column.type_cast(value) : value
-
end
-
-
1
def select_for_count
-
if @select_values.present?
-
select = @select_values.join(", ")
-
select if select !~ /(,|\*)/
-
end
-
end
-
-
1
def build_count_subquery(relation, column_name, distinct)
-
column_alias = Arel.sql('count_column')
-
subquery_alias = Arel.sql('subquery_for_count')
-
-
aliased_column = aggregate_column(column_name == :all ? 1 : column_name).as(column_alias)
-
relation.select_values = [aliased_column]
-
subquery = relation.arel.as(subquery_alias)
-
-
sm = Arel::SelectManager.new relation.engine
-
select_value = operation_over_aggregate_column(column_alias, 'count', distinct)
-
sm.project(select_value).from(subquery)
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
-
1
module ActiveRecord
-
1
module FinderMethods
-
# Find operates with four different retrieval approaches:
-
#
-
# * Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]).
-
# If no record can be found for all of the listed ids, then RecordNotFound will be raised.
-
# * Find first - This will return the first record matched by the options used. These options can either be specific
-
# conditions or merely an order. If no record can be matched, +nil+ is returned. Use
-
# <tt>Model.find(:first, *args)</tt> or its shortcut <tt>Model.first(*args)</tt>.
-
# * Find last - This will return the last record matched by the options used. These options can either be specific
-
# conditions or merely an order. If no record can be matched, +nil+ is returned. Use
-
# <tt>Model.find(:last, *args)</tt> or its shortcut <tt>Model.last(*args)</tt>.
-
# * Find all - This will return all the records matched by the options used.
-
# If no records are found, an empty array is returned. Use
-
# <tt>Model.find(:all, *args)</tt> or its shortcut <tt>Model.all(*args)</tt>.
-
#
-
# All approaches accept an options hash as their last parameter.
-
#
-
# ==== Options
-
#
-
# * <tt>:conditions</tt> - An SQL fragment like "administrator = 1", <tt>["user_name = ?", username]</tt>,
-
# or <tt>["user_name = :user_name", { :user_name => user_name }]</tt>. See conditions in the intro.
-
# * <tt>:order</tt> - An SQL fragment like "created_at DESC, name".
-
# * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
-
# * <tt>:having</tt> - Combined with +:group+ this can be used to filter the records that a
-
# <tt>GROUP BY</tt> returns. Uses the <tt>HAVING</tt> SQL-clause.
-
# * <tt>:limit</tt> - An integer determining the limit on the number of rows that should be returned.
-
# * <tt>:offset</tt> - An integer determining the offset from where the rows should be fetched. So at 5,
-
# it would skip rows 0 through 4.
-
# * <tt>:joins</tt> - Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed),
-
# named associations in the same form used for the <tt>:include</tt> option, which will perform an
-
# <tt>INNER JOIN</tt> on the associated table(s),
-
# or an array containing a mixture of both strings and named associations.
-
# If the value is a string, then the records will be returned read-only since they will
-
# have attributes that do not correspond to the table's columns.
-
# Pass <tt>:readonly => false</tt> to override.
-
# * <tt>:include</tt> - Names associations that should be loaded alongside. The symbols named refer
-
# to already defined associations. See eager loading under Associations.
-
# * <tt>:select</tt> - By default, this is "*" as in "SELECT * FROM", but can be changed if you,
-
# for example, want to do a join but not include the joined columns. Takes a string with the SELECT SQL fragment (e.g. "id, name").
-
# * <tt>:from</tt> - By default, this is the table name of the class, but can be changed
-
# to an alternate table name (or even the name of a database view).
-
# * <tt>:readonly</tt> - Mark the returned records read-only so they cannot be saved or updated.
-
# * <tt>:lock</tt> - An SQL fragment like "FOR UPDATE" or "LOCK IN SHARE MODE".
-
# <tt>:lock => true</tt> gives connection's default exclusive lock, usually "FOR UPDATE".
-
#
-
# ==== Examples
-
#
-
# # find by id
-
# Person.find(1) # returns the object for ID = 1
-
# Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6)
-
# Person.find([7, 17]) # returns an array for objects with IDs in (7, 17)
-
# Person.find([1]) # returns an array for the object with ID = 1
-
# Person.where("administrator = 1").order("created_on DESC").find(1)
-
#
-
# Note that returned records may not be in the same order as the ids you
-
# provide since database rows are unordered. Give an explicit <tt>:order</tt>
-
# to ensure the results are sorted.
-
#
-
# ==== Examples
-
#
-
# # find first
-
# Person.first # returns the first object fetched by SELECT * FROM people
-
# Person.where(["user_name = ?", user_name]).first
-
# Person.where(["user_name = :u", { :u => user_name }]).first
-
# Person.order("created_on DESC").offset(5).first
-
#
-
# # find last
-
# Person.last # returns the last object fetched by SELECT * FROM people
-
# Person.where(["user_name = ?", user_name]).last
-
# Person.order("created_on DESC").offset(5).last
-
#
-
# # find all
-
# Person.all # returns an array of objects for all the rows fetched by SELECT * FROM people
-
# Person.where(["category IN (?)", categories]).limit(50).all
-
# Person.where({ :friends => ["Bob", "Steve", "Fred"] }).all
-
# Person.offset(10).limit(10).all
-
# Person.includes([:account, :friends]).all
-
# Person.group("category").all
-
#
-
# Example for find with a lock: Imagine two concurrent transactions:
-
# each will read <tt>person.visits == 2</tt>, add 1 to it, and save, resulting
-
# in two saves of <tt>person.visits = 3</tt>. By locking the row, the second
-
# transaction has to wait until the first is finished; we get the
-
# expected <tt>person.visits == 4</tt>.
-
#
-
# Person.transaction do
-
# person = Person.lock(true).find(1)
-
# person.visits += 1
-
# person.save!
-
# end
-
1
def find(*args)
-
return to_a.find { |*block_args| yield(*block_args) } if block_given?
-
-
options = args.extract_options!
-
-
if options.present?
-
apply_finder_options(options).find(*args)
-
else
-
case args.first
-
when :first, :last, :all
-
send(args.first)
-
else
-
find_with_ids(*args)
-
end
-
end
-
end
-
-
# A convenience wrapper for <tt>find(:first, *args)</tt>. You can pass in all the
-
# same arguments to this method as you can to <tt>find(:first)</tt>.
-
1
def first(*args)
-
if args.any?
-
if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash))
-
to_a.first(*args)
-
else
-
apply_finder_options(args.first).first
-
end
-
else
-
find_first
-
end
-
end
-
-
# Same as +first+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
-
# is found. Note that <tt>first!</tt> accepts no arguments.
-
1
def first!
-
first or raise RecordNotFound
-
end
-
-
# A convenience wrapper for <tt>find(:last, *args)</tt>. You can pass in all the
-
# same arguments to this method as you can to <tt>find(:last)</tt>.
-
1
def last(*args)
-
if args.any?
-
if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash))
-
to_a.last(*args)
-
else
-
apply_finder_options(args.first).last
-
end
-
else
-
find_last
-
end
-
end
-
-
# Same as +last+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
-
# is found. Note that <tt>last!</tt> accepts no arguments.
-
1
def last!
-
last or raise RecordNotFound
-
end
-
-
# A convenience wrapper for <tt>find(:all, *args)</tt>. You can pass in all the
-
# same arguments to this method as you can to <tt>find(:all)</tt>.
-
1
def all(*args)
-
args.any? ? apply_finder_options(args.first).to_a : to_a
-
end
-
-
# Returns true if a record exists in the table that matches the +id+ or
-
# conditions given, or false otherwise. The argument can take five forms:
-
#
-
# * Integer - Finds the record with this primary key.
-
# * String - Finds the record with a primary key corresponding to this
-
# string (such as <tt>'5'</tt>).
-
# * Array - Finds the record that matches these +find+-style conditions
-
# (such as <tt>['color = ?', 'red']</tt>).
-
# * Hash - Finds the record that matches these +find+-style conditions
-
# (such as <tt>{:color => 'red'}</tt>).
-
# * No args - Returns false if the table is empty, true otherwise.
-
#
-
# For more information about specifying conditions as a Hash or Array,
-
# see the Conditions section in the introduction to ActiveRecord::Base.
-
#
-
# Note: You can't pass in a condition as a string (like <tt>name =
-
# 'Jamie'</tt>), since it would be sanitized and then queried against
-
# the primary key column, like <tt>id = 'name = \'Jamie\''</tt>.
-
#
-
# ==== Examples
-
# Person.exists?(5)
-
# Person.exists?('5')
-
# Person.exists?(:name => "David")
-
# Person.exists?(['name LIKE ?', "%#{query}%"])
-
# Person.exists?
-
1
def exists?(id = nil)
-
id = id.id if ActiveRecord::Base === id
-
-
join_dependency = construct_join_dependency_for_association_find
-
relation = construct_relation_for_association_find(join_dependency)
-
relation = relation.except(:select).select("1").limit(1)
-
-
case id
-
when Array, Hash
-
relation = relation.where(id)
-
else
-
relation = relation.where(table[primary_key].eq(id)) if id
-
end
-
-
connection.select_value(relation) ? true : false
-
end
-
-
1
protected
-
-
1
def find_with_associations
-
join_dependency = construct_join_dependency_for_association_find
-
relation = construct_relation_for_association_find(join_dependency)
-
rows = connection.select_all(relation, 'SQL', relation.bind_values)
-
join_dependency.instantiate(rows)
-
rescue ThrowResult
-
[]
-
end
-
-
1
def construct_join_dependency_for_association_find
-
including = (@eager_load_values + @includes_values).uniq
-
ActiveRecord::Associations::JoinDependency.new(@klass, including, [])
-
end
-
-
1
def construct_relation_for_association_calculations
-
including = (@eager_load_values + @includes_values).uniq
-
join_dependency = ActiveRecord::Associations::JoinDependency.new(@klass, including, arel.froms.first)
-
relation = except(:includes, :eager_load, :preload)
-
apply_join_dependency(relation, join_dependency)
-
end
-
-
1
def construct_relation_for_association_find(join_dependency)
-
relation = except(:includes, :eager_load, :preload, :select).select(join_dependency.columns)
-
apply_join_dependency(relation, join_dependency)
-
end
-
-
1
def apply_join_dependency(relation, join_dependency)
-
join_dependency.join_associations.each do |association|
-
relation = association.join_relation(relation)
-
end
-
-
limitable_reflections = using_limitable_reflections?(join_dependency.reflections)
-
-
if !limitable_reflections && relation.limit_value
-
limited_id_condition = construct_limited_ids_condition(relation.except(:select))
-
relation = relation.where(limited_id_condition)
-
end
-
-
relation = relation.except(:limit, :offset) unless limitable_reflections
-
-
relation
-
end
-
-
1
def construct_limited_ids_condition(relation)
-
orders = relation.order_values.map { |val| val.presence }.compact
-
values = @klass.connection.distinct("#{@klass.connection.quote_table_name table_name}.#{primary_key}", orders)
-
-
relation = relation.dup
-
-
ids_array = relation.select(values).collect {|row| row[primary_key]}
-
ids_array.empty? ? raise(ThrowResult) : table[primary_key].in(ids_array)
-
end
-
-
1
def find_by_attributes(match, attributes, *args)
-
conditions = Hash[attributes.map {|a| [a, args[attributes.index(a)]]}]
-
result = where(conditions).send(match.finder)
-
-
if match.bang? && result.blank?
-
raise RecordNotFound, "Couldn't find #{@klass.name} with #{conditions.to_a.collect {|p| p.join(' = ')}.join(', ')}"
-
else
-
result
-
end
-
end
-
-
1
def find_or_instantiator_by_attributes(match, attributes, *args)
-
options = args.size > 1 && args.last(2).all?{ |a| a.is_a?(Hash) } ? args.extract_options! : {}
-
protected_attributes_for_create, unprotected_attributes_for_create = {}, {}
-
args.each_with_index do |arg, i|
-
if arg.is_a?(Hash)
-
protected_attributes_for_create = args[i].with_indifferent_access
-
else
-
unprotected_attributes_for_create[attributes[i]] = args[i]
-
end
-
end
-
-
conditions = (protected_attributes_for_create.merge(unprotected_attributes_for_create)).slice(*attributes).symbolize_keys
-
-
record = where(conditions).first
-
-
unless record
-
record = @klass.new(protected_attributes_for_create, options) do |r|
-
r.assign_attributes(unprotected_attributes_for_create, :without_protection => true)
-
end
-
yield(record) if block_given?
-
record.save if match.instantiator == :create
-
end
-
-
record
-
end
-
-
1
def find_with_ids(*ids)
-
return to_a.find { |*block_args| yield(*block_args) } if block_given?
-
-
expects_array = ids.first.kind_of?(Array)
-
return ids.first if expects_array && ids.first.empty?
-
-
ids = ids.flatten.compact.uniq
-
-
case ids.size
-
when 0
-
raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
-
when 1
-
result = find_one(ids.first)
-
expects_array ? [ result ] : result
-
else
-
find_some(ids)
-
end
-
end
-
-
1
def find_one(id)
-
id = id.id if ActiveRecord::Base === id
-
-
if IdentityMap.enabled? && where_values.blank? &&
-
limit_value.blank? && order_values.blank? &&
-
includes_values.blank? && preload_values.blank? &&
-
readonly_value.nil? && joins_values.blank? &&
-
!@klass.locking_enabled? &&
-
record = IdentityMap.get(@klass, id)
-
return record
-
end
-
-
column = columns_hash[primary_key]
-
-
substitute = connection.substitute_at(column, @bind_values.length)
-
relation = where(table[primary_key].eq(substitute))
-
relation.bind_values = [[column, id]]
-
record = relation.first
-
-
unless record
-
conditions = arel.where_sql
-
conditions = " [#{conditions}]" if conditions
-
raise RecordNotFound, "Couldn't find #{@klass.name} with #{primary_key}=#{id}#{conditions}"
-
end
-
-
record
-
end
-
-
1
def find_some(ids)
-
result = where(table[primary_key].in(ids)).all
-
-
expected_size =
-
if @limit_value && ids.size > @limit_value
-
@limit_value
-
else
-
ids.size
-
end
-
-
# 11 ids with limit 3, offset 9 should give 2 results.
-
if @offset_value && (ids.size - @offset_value < expected_size)
-
expected_size = ids.size - @offset_value
-
end
-
-
if result.size == expected_size
-
result
-
else
-
conditions = arel.where_sql
-
conditions = " [#{conditions}]" if conditions
-
-
error = "Couldn't find all #{@klass.name.pluralize} with IDs "
-
error << "(#{ids.join(", ")})#{conditions} (found #{result.size} results, but was looking for #{expected_size})"
-
raise RecordNotFound, error
-
end
-
end
-
-
1
def find_first
-
if loaded?
-
@records.first
-
else
-
@first ||= limit(1).to_a[0]
-
end
-
end
-
-
1
def find_last
-
if loaded?
-
@records.last
-
else
-
@last ||=
-
if offset_value || limit_value
-
to_a.last
-
else
-
reverse_order.limit(1).to_a[0]
-
end
-
end
-
end
-
-
1
def using_limitable_reflections?(reflections)
-
reflections.none? { |r| r.collection? }
-
end
-
end
-
end
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_support/core_ext/object/blank'
-
-
1
module ActiveRecord
-
1
module QueryMethods
-
1
extend ActiveSupport::Concern
-
-
1
attr_accessor :includes_values, :eager_load_values, :preload_values,
-
:select_values, :group_values, :order_values, :joins_values,
-
:where_values, :having_values, :bind_values,
-
:limit_value, :offset_value, :lock_value, :readonly_value, :create_with_value,
-
:from_value, :reorder_value, :reverse_order_value
-
-
1
def includes(*args)
-
args.reject! {|a| a.blank? }
-
-
return self if args.empty?
-
-
relation = clone
-
relation.includes_values = (relation.includes_values + args).flatten.uniq
-
relation
-
end
-
-
1
def eager_load(*args)
-
return self if args.blank?
-
-
relation = clone
-
relation.eager_load_values += args
-
relation
-
end
-
-
1
def preload(*args)
-
return self if args.blank?
-
-
relation = clone
-
relation.preload_values += args
-
relation
-
end
-
-
1
def select(value = Proc.new)
-
if block_given?
-
to_a.select {|*block_args| value.call(*block_args) }
-
else
-
relation = clone
-
relation.select_values += Array.wrap(value)
-
relation
-
end
-
end
-
-
1
def group(*args)
-
return self if args.blank?
-
-
relation = clone
-
relation.group_values += args.flatten
-
relation
-
end
-
-
1
def order(*args)
-
return self if args.blank?
-
-
relation = clone
-
relation.order_values += args.flatten
-
relation
-
end
-
-
1
def reorder(*args)
-
return self if args.blank?
-
-
relation = clone
-
relation.reorder_value = args.flatten
-
relation
-
end
-
-
1
def joins(*args)
-
return self if args.compact.blank?
-
-
relation = clone
-
-
args.flatten!
-
relation.joins_values += args
-
-
relation
-
end
-
-
1
def bind(value)
-
relation = clone
-
relation.bind_values += [value]
-
relation
-
end
-
-
1
def where(opts, *rest)
-
return self if opts.blank?
-
-
relation = clone
-
relation.where_values += build_where(opts, rest)
-
relation
-
end
-
-
1
def having(opts, *rest)
-
return self if opts.blank?
-
-
relation = clone
-
relation.having_values += build_where(opts, rest)
-
relation
-
end
-
-
1
def limit(value)
-
relation = clone
-
relation.limit_value = value
-
relation
-
end
-
-
1
def offset(value)
-
relation = clone
-
relation.offset_value = value
-
relation
-
end
-
-
1
def lock(locks = true)
-
relation = clone
-
-
case locks
-
when String, TrueClass, NilClass
-
relation.lock_value = locks || true
-
else
-
relation.lock_value = false
-
end
-
-
relation
-
end
-
-
1
def readonly(value = true)
-
relation = clone
-
relation.readonly_value = value
-
relation
-
end
-
-
1
def create_with(value)
-
relation = clone
-
relation.create_with_value = value ? create_with_value.merge(value) : {}
-
relation
-
end
-
-
1
def from(value)
-
relation = clone
-
relation.from_value = value
-
relation
-
end
-
-
1
def extending(*modules)
-
modules << Module.new(&Proc.new) if block_given?
-
-
return self if modules.empty?
-
-
relation = clone
-
relation.send(:apply_modules, modules.flatten)
-
relation
-
end
-
-
1
def reverse_order
-
relation = clone
-
relation.reverse_order_value = !relation.reverse_order_value
-
relation
-
end
-
-
1
def arel
-
@arel ||= with_default_scope.build_arel
-
end
-
-
1
def build_arel
-
arel = table.from table
-
-
build_joins(arel, @joins_values) unless @joins_values.empty?
-
-
collapse_wheres(arel, (@where_values - ['']).uniq)
-
-
arel.having(*@having_values.uniq.reject{|h| h.blank?}) unless @having_values.empty?
-
-
arel.take(connection.sanitize_limit(@limit_value)) if @limit_value
-
arel.skip(@offset_value) if @offset_value
-
-
arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty?
-
-
order = @reorder_value ? @reorder_value : @order_values
-
order = reverse_sql_order(order) if @reverse_order_value
-
arel.order(*order.uniq.reject{|o| o.blank?}) unless order.empty?
-
-
build_select(arel, @select_values.uniq)
-
-
arel.from(@from_value) if @from_value
-
arel.lock(@lock_value) if @lock_value
-
-
arel
-
end
-
-
1
private
-
-
1
def custom_join_ast(table, joins)
-
joins = joins.reject { |join| join.blank? }
-
-
return [] if joins.empty?
-
-
@implicit_readonly = true
-
-
joins.map do |join|
-
case join
-
when Array
-
join = Arel.sql(join.join(' ')) if array_of_strings?(join)
-
when String
-
join = Arel.sql(join)
-
end
-
table.create_string_join(join)
-
end
-
end
-
-
1
def collapse_wheres(arel, wheres)
-
equalities = wheres.grep(Arel::Nodes::Equality)
-
-
arel.where(Arel::Nodes::And.new(equalities)) unless equalities.empty?
-
-
(wheres - equalities).each do |where|
-
where = Arel.sql(where) if String === where
-
arel.where(Arel::Nodes::Grouping.new(where))
-
end
-
end
-
-
1
def build_where(opts, other = [])
-
case opts
-
when String, Array
-
[@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
-
when Hash
-
attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
-
PredicateBuilder.build_from_hash(table.engine, attributes, table)
-
else
-
[opts]
-
end
-
end
-
-
1
def build_joins(manager, joins)
-
buckets = joins.group_by do |join|
-
case join
-
when String
-
'string_join'
-
when Hash, Symbol, Array
-
'association_join'
-
when ActiveRecord::Associations::JoinDependency::JoinAssociation
-
'stashed_join'
-
when Arel::Nodes::Join
-
'join_node'
-
else
-
raise 'unknown class: %s' % join.class.name
-
end
-
end
-
-
association_joins = buckets['association_join'] || []
-
stashed_association_joins = buckets['stashed_join'] || []
-
join_nodes = buckets['join_node'] || []
-
string_joins = (buckets['string_join'] || []).map { |x|
-
x.strip
-
}.uniq
-
-
join_list = custom_join_ast(manager, string_joins)
-
-
join_dependency = ActiveRecord::Associations::JoinDependency.new(
-
@klass,
-
association_joins,
-
join_list
-
)
-
-
join_nodes.each do |join|
-
join_dependency.alias_tracker.aliased_name_for(join.left.name.downcase)
-
end
-
-
join_dependency.graft(*stashed_association_joins)
-
-
@implicit_readonly = true unless association_joins.empty? && stashed_association_joins.empty?
-
-
# FIXME: refactor this to build an AST
-
join_dependency.join_associations.each do |association|
-
association.join_to(manager)
-
end
-
-
manager.join_sources.concat join_nodes.uniq
-
manager.join_sources.concat join_list
-
-
manager
-
end
-
-
1
def build_select(arel, selects)
-
unless selects.empty?
-
@implicit_readonly = false
-
arel.project(*selects)
-
else
-
arel.project(@klass.arel_table[Arel.star])
-
end
-
end
-
-
1
def apply_modules(modules)
-
unless modules.empty?
-
@extensions += modules
-
modules.each {|extension| extend(extension) }
-
end
-
end
-
-
1
def reverse_sql_order(order_query)
-
order_query = ["#{quoted_table_name}.#{quoted_primary_key} ASC"] if order_query.empty?
-
-
order_query.map do |o|
-
case o
-
when Arel::Nodes::Ascending, Arel::Nodes::Descending
-
o.reverse
-
when String, Symbol
-
o.to_s.split(',').collect do |s|
-
s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
-
end
-
else
-
o
-
end
-
end.flatten
-
end
-
-
1
def array_of_strings?(o)
-
o.is_a?(Array) && o.all?{|obj| obj.is_a?(String)}
-
end
-
-
end
-
end
-
1
require 'active_support/core_ext/object/blank'
-
-
1
module ActiveRecord
-
1
module SpawnMethods
-
1
def merge(r)
-
return self unless r
-
return to_a & r if r.is_a?(Array)
-
-
merged_relation = clone
-
-
r = r.with_default_scope if r.default_scoped? && r.klass != klass
-
-
Relation::ASSOCIATION_METHODS.each do |method|
-
value = r.send(:"#{method}_values")
-
-
unless value.empty?
-
if method == :includes
-
merged_relation = merged_relation.includes(value)
-
else
-
merged_relation.send(:"#{method}_values=", value)
-
end
-
end
-
end
-
-
(Relation::MULTI_VALUE_METHODS - [:joins, :where]).each do |method|
-
value = r.send(:"#{method}_values")
-
merged_relation.send(:"#{method}_values=", merged_relation.send(:"#{method}_values") + value) if value.present?
-
end
-
-
merged_relation.joins_values += r.joins_values
-
-
merged_wheres = @where_values + r.where_values
-
-
unless @where_values.empty?
-
# Remove duplicates, last one wins.
-
seen = Hash.new { |h,table| h[table] = {} }
-
merged_wheres = merged_wheres.reverse.reject { |w|
-
nuke = false
-
if w.respond_to?(:operator) && w.operator == :==
-
name = w.left.name
-
table = w.left.relation.name
-
nuke = seen[table][name]
-
seen[table][name] = true
-
end
-
nuke
-
}.reverse
-
end
-
-
merged_relation.where_values = merged_wheres
-
-
(Relation::SINGLE_VALUE_METHODS - [:lock, :create_with]).each do |method|
-
value = r.send(:"#{method}_value")
-
merged_relation.send(:"#{method}_value=", value) unless value.nil?
-
end
-
-
merged_relation.lock_value = r.lock_value unless merged_relation.lock_value
-
-
merged_relation = merged_relation.create_with(r.create_with_value) unless r.create_with_value.empty?
-
-
# Apply scope extension modules
-
merged_relation.send :apply_modules, r.extensions
-
-
merged_relation
-
end
-
-
# Removes from the query the condition(s) specified in +skips+.
-
#
-
# Example:
-
#
-
# Post.order('id asc').except(:order) # discards the order condition
-
# Post.where('id > 10').order('id asc').except(:where) # discards the where condition but keeps the order
-
#
-
1
def except(*skips)
-
result = self.class.new(@klass, table)
-
result.default_scoped = default_scoped
-
-
((Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS) - skips).each do |method|
-
result.send(:"#{method}_values=", send(:"#{method}_values"))
-
end
-
-
(Relation::SINGLE_VALUE_METHODS - skips).each do |method|
-
result.send(:"#{method}_value=", send(:"#{method}_value"))
-
end
-
-
# Apply scope extension modules
-
result.send(:apply_modules, extensions)
-
-
result
-
end
-
-
# Removes any condition from the query other than the one(s) specified in +onlies+.
-
#
-
# Example:
-
#
-
# Post.order('id asc').only(:where) # discards the order condition
-
# Post.order('id asc').only(:where, :order) # uses the specified order
-
#
-
1
def only(*onlies)
-
result = self.class.new(@klass, table)
-
result.default_scoped = default_scoped
-
-
((Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS) & onlies).each do |method|
-
result.send(:"#{method}_values=", send(:"#{method}_values"))
-
end
-
-
(Relation::SINGLE_VALUE_METHODS & onlies).each do |method|
-
result.send(:"#{method}_value=", send(:"#{method}_value"))
-
end
-
-
# Apply scope extension modules
-
result.send(:apply_modules, extensions)
-
-
result
-
end
-
-
1
VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset, :extend,
-
:order, :select, :readonly, :group, :having, :from, :lock ]
-
-
1
def apply_finder_options(options)
-
relation = clone
-
return relation unless options
-
-
options.assert_valid_keys(VALID_FIND_OPTIONS)
-
finders = options.dup
-
finders.delete_if { |key, value| value.nil? && key != :limit }
-
-
([:joins, :select, :group, :order, :having, :limit, :offset, :from, :lock, :readonly] & finders.keys).each do |finder|
-
relation = relation.send(finder, finders[finder])
-
end
-
-
relation = relation.where(finders[:conditions]) if options.has_key?(:conditions)
-
relation = relation.includes(finders[:include]) if options.has_key?(:include)
-
relation = relation.extending(finders[:extend]) if options.has_key?(:extend)
-
-
relation
-
end
-
-
end
-
end
-
1
module ActiveRecord
-
###
-
# This class encapsulates a Result returned from calling +exec_query+ on any
-
# database connection adapter. For example:
-
#
-
# x = ActiveRecord::Base.connection.exec_query('SELECT * FROM foo')
-
# x # => #<ActiveRecord::Result:0xdeadbeef>
-
1
class Result
-
1
include Enumerable
-
-
1
attr_reader :columns, :rows
-
-
1
def initialize(columns, rows)
-
9
@columns = columns
-
9
@rows = rows
-
9
@hash_rows = nil
-
end
-
-
1
def each
-
20
hash_rows.each { |row| yield row }
-
end
-
-
1
def to_hash
-
5
hash_rows
-
end
-
-
1
private
-
1
def hash_rows
-
@hash_rows ||= @rows.map { |row|
-
51
Hash[@columns.zip(row)]
-
9
}
-
end
-
end
-
end
-
1
module ActiveRecord #:nodoc:
-
# = Active Record Serialization
-
1
module Serialization
-
1
extend ActiveSupport::Concern
-
1
include ActiveModel::Serializers::JSON
-
-
1
def serializable_hash(options = nil)
-
options = options.try(:clone) || {}
-
-
options[:except] = Array.wrap(options[:except]).map { |n| n.to_s }
-
options[:except] |= Array.wrap(self.class.inheritance_column)
-
-
hash = super(options)
-
-
serializable_add_includes(options) do |association, records, opts|
-
hash[association] = records.is_a?(Enumerable) ?
-
records.map { |r| r.serializable_hash(opts) } :
-
records.serializable_hash(opts)
-
end
-
-
hash
-
end
-
-
1
private
-
# Add associations specified via the <tt>:include</tt> option.
-
#
-
# Expects a block that takes as arguments:
-
# +association+ - name of the association
-
# +records+ - the association record(s) to be serialized
-
# +opts+ - options for the association records
-
1
def serializable_add_includes(options = {})
-
return unless include_associations = options.delete(:include)
-
-
base_only_or_except = { :except => options[:except],
-
:only => options[:only] }
-
-
include_has_options = include_associations.is_a?(Hash)
-
associations = include_has_options ? include_associations.keys : Array.wrap(include_associations)
-
-
associations.each do |association|
-
records = case self.class.reflect_on_association(association).macro
-
when :has_many, :has_and_belongs_to_many
-
send(association).to_a
-
when :has_one, :belongs_to
-
send(association)
-
end
-
-
if records
-
association_options = include_has_options ? include_associations[association] : base_only_or_except
-
opts = options.merge(association_options)
-
yield(association, records, opts)
-
end
-
end
-
-
options[:include] = include_associations
-
end
-
end
-
end
-
-
1
require 'active_record/serializers/xml_serializer'
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_support/core_ext/hash/conversions'
-
-
1
module ActiveRecord #:nodoc:
-
1
module Serialization
-
1
include ActiveModel::Serializers::Xml
-
-
# Builds an XML document to represent the model. Some configuration is
-
# available through +options+. However more complicated cases should
-
# override ActiveRecord::Base#to_xml.
-
#
-
# By default the generated XML document will include the processing
-
# instruction and all the object's attributes. For example:
-
#
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <topic>
-
# <title>The First Topic</title>
-
# <author-name>David</author-name>
-
# <id type="integer">1</id>
-
# <approved type="boolean">false</approved>
-
# <replies-count type="integer">0</replies-count>
-
# <bonus-time type="datetime">2000-01-01T08:28:00+12:00</bonus-time>
-
# <written-on type="datetime">2003-07-16T09:28:00+1200</written-on>
-
# <content>Have a nice day</content>
-
# <author-email-address>david@loudthinking.com</author-email-address>
-
# <parent-id></parent-id>
-
# <last-read type="date">2004-04-15</last-read>
-
# </topic>
-
#
-
# This behavior can be controlled with <tt>:only</tt>, <tt>:except</tt>,
-
# <tt>:skip_instruct</tt>, <tt>:skip_types</tt>, <tt>:dasherize</tt> and <tt>:camelize</tt> .
-
# The <tt>:only</tt> and <tt>:except</tt> options are the same as for the
-
# +attributes+ method. The default is to dasherize all column names, but you
-
# can disable this setting <tt>:dasherize</tt> to +false+. Setting <tt>:camelize</tt>
-
# to +true+ will camelize all column names - this also overrides <tt>:dasherize</tt>.
-
# To not have the column type included in the XML output set <tt>:skip_types</tt> to +true+.
-
#
-
# For instance:
-
#
-
# topic.to_xml(:skip_instruct => true, :except => [ :id, :bonus_time, :written_on, :replies_count ])
-
#
-
# <topic>
-
# <title>The First Topic</title>
-
# <author-name>David</author-name>
-
# <approved type="boolean">false</approved>
-
# <content>Have a nice day</content>
-
# <author-email-address>david@loudthinking.com</author-email-address>
-
# <parent-id></parent-id>
-
# <last-read type="date">2004-04-15</last-read>
-
# </topic>
-
#
-
# To include first level associations use <tt>:include</tt>:
-
#
-
# firm.to_xml :include => [ :account, :clients ]
-
#
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <firm>
-
# <id type="integer">1</id>
-
# <rating type="integer">1</rating>
-
# <name>37signals</name>
-
# <clients type="array">
-
# <client>
-
# <rating type="integer">1</rating>
-
# <name>Summit</name>
-
# </client>
-
# <client>
-
# <rating type="integer">1</rating>
-
# <name>Microsoft</name>
-
# </client>
-
# </clients>
-
# <account>
-
# <id type="integer">1</id>
-
# <credit-limit type="integer">50</credit-limit>
-
# </account>
-
# </firm>
-
#
-
# Additionally, the record being serialized will be passed to a Proc's second
-
# parameter. This allows for ad hoc additions to the resultant document that
-
# incorporate the context of the record being serialized. And by leveraging the
-
# closure created by a Proc, to_xml can be used to add elements that normally fall
-
# outside of the scope of the model -- for example, generating and appending URLs
-
# associated with models.
-
#
-
# proc = Proc.new { |options, record| options[:builder].tag!('name-reverse', record.name.reverse) }
-
# firm.to_xml :procs => [ proc ]
-
#
-
# <firm>
-
# # ... normal attributes as shown above ...
-
# <name-reverse>slangis73</name-reverse>
-
# </firm>
-
#
-
# To include deeper levels of associations pass a hash like this:
-
#
-
# firm.to_xml :include => {:account => {}, :clients => {:include => :address}}
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <firm>
-
# <id type="integer">1</id>
-
# <rating type="integer">1</rating>
-
# <name>37signals</name>
-
# <clients type="array">
-
# <client>
-
# <rating type="integer">1</rating>
-
# <name>Summit</name>
-
# <address>
-
# ...
-
# </address>
-
# </client>
-
# <client>
-
# <rating type="integer">1</rating>
-
# <name>Microsoft</name>
-
# <address>
-
# ...
-
# </address>
-
# </client>
-
# </clients>
-
# <account>
-
# <id type="integer">1</id>
-
# <credit-limit type="integer">50</credit-limit>
-
# </account>
-
# </firm>
-
#
-
# To include any methods on the model being called use <tt>:methods</tt>:
-
#
-
# firm.to_xml :methods => [ :calculated_earnings, :real_earnings ]
-
#
-
# <firm>
-
# # ... normal attributes as shown above ...
-
# <calculated-earnings>100000000000000000</calculated-earnings>
-
# <real-earnings>5</real-earnings>
-
# </firm>
-
#
-
# To call any additional Procs use <tt>:procs</tt>. The Procs are passed a
-
# modified version of the options hash that was given to +to_xml+:
-
#
-
# proc = Proc.new { |options| options[:builder].tag!('abc', 'def') }
-
# firm.to_xml :procs => [ proc ]
-
#
-
# <firm>
-
# # ... normal attributes as shown above ...
-
# <abc>def</abc>
-
# </firm>
-
#
-
# Alternatively, you can yield the builder object as part of the +to_xml+ call:
-
#
-
# firm.to_xml do |xml|
-
# xml.creator do
-
# xml.first_name "David"
-
# xml.last_name "Heinemeier Hansson"
-
# end
-
# end
-
#
-
# <firm>
-
# # ... normal attributes as shown above ...
-
# <creator>
-
# <first_name>David</first_name>
-
# <last_name>Heinemeier Hansson</last_name>
-
# </creator>
-
# </firm>
-
#
-
# As noted above, you may override +to_xml+ in your ActiveRecord::Base
-
# subclasses to have complete control about what's generated. The general
-
# form of doing this is:
-
#
-
# class IHaveMyOwnXML < ActiveRecord::Base
-
# def to_xml(options = {})
-
# options[:indent] ||= 2
-
# xml = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
-
# xml.instruct! unless options[:skip_instruct]
-
# xml.level_one do
-
# xml.tag!(:second_level, 'content')
-
# end
-
# end
-
# end
-
1
def to_xml(options = {}, &block)
-
XmlSerializer.new(self, options).serialize(&block)
-
end
-
end
-
-
1
class XmlSerializer < ActiveModel::Serializers::Xml::Serializer #:nodoc:
-
1
def initialize(*args)
-
super
-
options[:except] |= Array.wrap(@serializable.class.inheritance_column)
-
end
-
-
1
def add_extra_behavior
-
add_includes
-
end
-
-
1
def add_includes
-
procs = options.delete(:procs)
-
@serializable.send(:serializable_add_includes, options) do |association, records, opts|
-
add_associations(association, records, opts)
-
end
-
options[:procs] = procs
-
end
-
-
# TODO This can likely be cleaned up to simple use ActiveSupport::XmlMini.to_tag as well.
-
1
def add_associations(association, records, opts)
-
association_name = association.to_s.singularize
-
merged_options = options.merge(opts).merge!(:root => association_name, :skip_instruct => true)
-
-
if records.is_a?(Enumerable)
-
tag = ActiveSupport::XmlMini.rename_key(association.to_s, options)
-
type = options[:skip_types] ? { } : {:type => "array"}
-
-
if records.empty?
-
@builder.tag!(tag, type)
-
else
-
@builder.tag!(tag, type) do
-
records.each do |record|
-
if options[:skip_types]
-
record_type = {}
-
else
-
record_class = (record.class.to_s.underscore == association_name) ? nil : record.class.name
-
record_type = {:type => record_class}
-
end
-
-
record.to_xml merged_options.merge(record_type)
-
end
-
end
-
end
-
elsif record = @serializable.send(association)
-
record.to_xml(merged_options)
-
end
-
end
-
-
1
class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc:
-
1
def compute_type
-
klass = @serializable.class
-
type = if klass.serialized_attributes.key?(name)
-
super
-
elsif klass.columns_hash.key?(name)
-
klass.columns_hash[name].type
-
else
-
NilClass
-
end
-
-
{ :text => :string,
-
:time => :datetime }[type] || type
-
end
-
1
protected :compute_type
-
end
-
end
-
end
-
1
module ActiveRecord
-
# = Active Record Test Case
-
#
-
# Defines some test assertions to test against SQL queries.
-
1
class TestCase < ActiveSupport::TestCase #:nodoc:
-
1
setup :cleanup_identity_map
-
-
1
def setup
-
cleanup_identity_map
-
end
-
-
1
def cleanup_identity_map
-
ActiveRecord::IdentityMap.clear
-
end
-
-
# Backport skip to Ruby 1.8. test/unit doesn't support it, so just
-
# make it a noop.
-
1
unless instance_methods.map(&:to_s).include?("skip")
-
def skip(message)
-
end
-
end
-
-
1
def assert_date_from_db(expected, actual, message = nil)
-
# SybaseAdapter doesn't have a separate column type just for dates,
-
# so the time is in the string and incorrectly formatted
-
if current_adapter?(:SybaseAdapter)
-
assert_equal expected.to_s, actual.to_date.to_s, message
-
else
-
assert_equal expected.to_s, actual.to_s, message
-
end
-
end
-
-
1
def assert_sql(*patterns_to_match)
-
$queries_executed = []
-
yield
-
$queries_executed
-
ensure
-
failed_patterns = []
-
patterns_to_match.each do |pattern|
-
failed_patterns << pattern unless $queries_executed.any?{ |sql| pattern === sql }
-
end
-
assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map{ |p| p.inspect }.join(', ')} not found.#{$queries_executed.size == 0 ? '' : "\nQueries:\n#{$queries_executed.join("\n")}"}"
-
end
-
-
1
def assert_queries(num = 1)
-
$queries_executed = []
-
yield
-
ensure
-
assert_equal num, $queries_executed.size, "#{$queries_executed.size} instead of #{num} queries were executed.#{$queries_executed.size == 0 ? '' : "\nQueries:\n#{$queries_executed.join("\n")}"}"
-
end
-
-
1
def assert_no_queries(&block)
-
assert_queries(0, &block)
-
end
-
-
1
def with_kcode(kcode)
-
if RUBY_VERSION < '1.9'
-
orig_kcode, $KCODE = $KCODE, kcode
-
begin
-
yield
-
ensure
-
$KCODE = orig_kcode
-
end
-
else
-
yield
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/class/attribute'
-
-
1
module ActiveRecord
-
# = Active Record Timestamp
-
#
-
# Active Record automatically timestamps create and update operations if the
-
# table has fields named <tt>created_at/created_on</tt> or
-
# <tt>updated_at/updated_on</tt>.
-
#
-
# Timestamping can be turned off by setting:
-
#
-
# config.active_record.record_timestamps = false
-
#
-
# Timestamps are in the local timezone by default but you can use UTC by setting:
-
#
-
# config.active_record.default_timezone = :utc
-
#
-
# == Time Zone aware attributes
-
#
-
# By default, ActiveRecord::Base keeps all the datetime columns time zone aware by executing following code.
-
#
-
# config.active_record.time_zone_aware_attributes = true
-
#
-
# This feature can easily be turned off by assigning value <tt>false</tt> .
-
#
-
# If your attributes are time zone aware and you desire to skip time zone conversion to the current Time.zone
-
# when reading certain attributes then you can do following:
-
#
-
# class Topic < ActiveRecord::Base
-
# self.skip_time_zone_conversion_for_attributes = [:written_on]
-
# end
-
1
module Timestamp
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
class_attribute :record_timestamps, :instance_writer => false
-
1
self.record_timestamps = true
-
end
-
-
1
private
-
-
1
def create #:nodoc:
-
if self.record_timestamps
-
current_time = current_time_from_proper_timezone
-
-
all_timestamp_attributes.each do |column|
-
write_attribute(column.to_s, current_time) if respond_to?(column) && self.send(column).nil?
-
end
-
end
-
-
super
-
end
-
-
1
def update(*args) #:nodoc:
-
if should_record_timestamps?
-
current_time = current_time_from_proper_timezone
-
-
timestamp_attributes_for_update_in_model.each do |column|
-
column = column.to_s
-
next if attribute_changed?(column)
-
write_attribute(column, current_time)
-
end
-
end
-
super
-
end
-
-
1
def should_record_timestamps?
-
self.record_timestamps && (!partial_updates? || changed? || (attributes.keys & self.class.serialized_attributes.keys).present?)
-
end
-
-
1
def timestamp_attributes_for_create_in_model
-
timestamp_attributes_for_create.select { |c| self.class.column_names.include?(c.to_s) }
-
end
-
-
1
def timestamp_attributes_for_update_in_model
-
timestamp_attributes_for_update.select { |c| self.class.column_names.include?(c.to_s) }
-
end
-
-
1
def all_timestamp_attributes_in_model
-
timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model
-
end
-
-
1
def timestamp_attributes_for_update #:nodoc:
-
[:updated_at, :updated_on]
-
end
-
-
1
def timestamp_attributes_for_create #:nodoc:
-
[:created_at, :created_on]
-
end
-
-
1
def all_timestamp_attributes #:nodoc:
-
timestamp_attributes_for_create + timestamp_attributes_for_update
-
end
-
-
1
def current_time_from_proper_timezone #:nodoc:
-
self.class.default_timezone == :utc ? Time.now.utc : Time.now
-
end
-
end
-
end
-
-
1
require 'thread'
-
-
1
module ActiveRecord
-
# See ActiveRecord::Transactions::ClassMethods for documentation.
-
1
module Transactions
-
1
extend ActiveSupport::Concern
-
-
1
class TransactionError < ActiveRecordError # :nodoc:
-
end
-
-
1
included do
-
1
define_callbacks :commit, :rollback, :terminator => "result == false", :scope => [:kind, :name]
-
end
-
-
# = Active Record Transactions
-
#
-
# Transactions are protective blocks where SQL statements are only permanent
-
# if they can all succeed as one atomic action. The classic example is a
-
# transfer between two accounts where you can only have a deposit if the
-
# withdrawal succeeded and vice versa. Transactions enforce the integrity of
-
# the database and guard the data against program errors or database
-
# break-downs. So basically you should use transaction blocks whenever you
-
# have a number of statements that must be executed together or not at all.
-
#
-
# For example:
-
#
-
# ActiveRecord::Base.transaction do
-
# david.withdrawal(100)
-
# mary.deposit(100)
-
# end
-
#
-
# This example will only take money from David and give it to Mary if neither
-
# +withdrawal+ nor +deposit+ raise an exception. Exceptions will force a
-
# ROLLBACK that returns the database to the state before the transaction
-
# began. Be aware, though, that the objects will _not_ have their instance
-
# data returned to their pre-transactional state.
-
#
-
# == Different Active Record classes in a single transaction
-
#
-
# Though the transaction class method is called on some Active Record class,
-
# the objects within the transaction block need not all be instances of
-
# that class. This is because transactions are per-database connection, not
-
# per-model.
-
#
-
# In this example a +balance+ record is transactionally saved even
-
# though +transaction+ is called on the +Account+ class:
-
#
-
# Account.transaction do
-
# balance.save!
-
# account.save!
-
# end
-
#
-
# The +transaction+ method is also available as a model instance method.
-
# For example, you can also do this:
-
#
-
# balance.transaction do
-
# balance.save!
-
# account.save!
-
# end
-
#
-
# == Transactions are not distributed across database connections
-
#
-
# A transaction acts on a single database connection. If you have
-
# multiple class-specific databases, the transaction will not protect
-
# interaction among them. One workaround is to begin a transaction
-
# on each class whose models you alter:
-
#
-
# Student.transaction do
-
# Course.transaction do
-
# course.enroll(student)
-
# student.units += course.units
-
# end
-
# end
-
#
-
# This is a poor solution, but fully distributed transactions are beyond
-
# the scope of Active Record.
-
#
-
# == +save+ and +destroy+ are automatically wrapped in a transaction
-
#
-
# Both +save+ and +destroy+ come wrapped in a transaction that ensures
-
# that whatever you do in validations or callbacks will happen under its
-
# protected cover. So you can use validations to check for values that
-
# the transaction depends on or you can raise exceptions in the callbacks
-
# to rollback, including <tt>after_*</tt> callbacks.
-
#
-
# As a consequence changes to the database are not seen outside your connection
-
# until the operation is complete. For example, if you try to update the index
-
# of a search engine in +after_save+ the indexer won't see the updated record.
-
# The +after_commit+ callback is the only one that is triggered once the update
-
# is committed. See below.
-
#
-
# == Exception handling and rolling back
-
#
-
# Also have in mind that exceptions thrown within a transaction block will
-
# be propagated (after triggering the ROLLBACK), so you should be ready to
-
# catch those in your application code.
-
#
-
# One exception is the <tt>ActiveRecord::Rollback</tt> exception, which will trigger
-
# a ROLLBACK when raised, but not be re-raised by the transaction block.
-
#
-
# *Warning*: one should not catch <tt>ActiveRecord::StatementInvalid</tt> exceptions
-
# inside a transaction block. <tt>ActiveRecord::StatementInvalid</tt> exceptions indicate that an
-
# error occurred at the database level, for example when a unique constraint
-
# is violated. On some database systems, such as PostgreSQL, database errors
-
# inside a transaction cause the entire transaction to become unusable
-
# until it's restarted from the beginning. Here is an example which
-
# demonstrates the problem:
-
#
-
# # Suppose that we have a Number model with a unique column called 'i'.
-
# Number.transaction do
-
# Number.create(:i => 0)
-
# begin
-
# # This will raise a unique constraint error...
-
# Number.create(:i => 0)
-
# rescue ActiveRecord::StatementInvalid
-
# # ...which we ignore.
-
# end
-
#
-
# # On PostgreSQL, the transaction is now unusable. The following
-
# # statement will cause a PostgreSQL error, even though the unique
-
# # constraint is no longer violated:
-
# Number.create(:i => 1)
-
# # => "PGError: ERROR: current transaction is aborted, commands
-
# # ignored until end of transaction block"
-
# end
-
#
-
# One should restart the entire transaction if an
-
# <tt>ActiveRecord::StatementInvalid</tt> occurred.
-
#
-
# == Nested transactions
-
#
-
# +transaction+ calls can be nested. By default, this makes all database
-
# statements in the nested transaction block become part of the parent
-
# transaction. For example, the following behavior may be surprising:
-
#
-
# User.transaction do
-
# User.create(:username => 'Kotori')
-
# User.transaction do
-
# User.create(:username => 'Nemu')
-
# raise ActiveRecord::Rollback
-
# end
-
# end
-
#
-
# creates both "Kotori" and "Nemu". Reason is the <tt>ActiveRecord::Rollback</tt>
-
# exception in the nested block does not issue a ROLLBACK. Since these exceptions
-
# are captured in transaction blocks, the parent block does not see it and the
-
# real transaction is committed.
-
#
-
# In order to get a ROLLBACK for the nested transaction you may ask for a real
-
# sub-transaction by passing <tt>:requires_new => true</tt>. If anything goes wrong,
-
# the database rolls back to the beginning of the sub-transaction without rolling
-
# back the parent transaction. If we add it to the previous example:
-
#
-
# User.transaction do
-
# User.create(:username => 'Kotori')
-
# User.transaction(:requires_new => true) do
-
# User.create(:username => 'Nemu')
-
# raise ActiveRecord::Rollback
-
# end
-
# end
-
#
-
# only "Kotori" is created. (This works on MySQL and PostgreSQL, but not on SQLite3.)
-
#
-
# Most databases don't support true nested transactions. At the time of
-
# writing, the only database that we're aware of that supports true nested
-
# transactions, is MS-SQL. Because of this, Active Record emulates nested
-
# transactions by using savepoints on MySQL and PostgreSQL. See
-
# http://dev.mysql.com/doc/refman/5.0/en/savepoint.html
-
# for more information about savepoints.
-
#
-
# === Callbacks
-
#
-
# There are two types of callbacks associated with committing and rolling back transactions:
-
# +after_commit+ and +after_rollback+.
-
#
-
# +after_commit+ callbacks are called on every record saved or destroyed within a
-
# transaction immediately after the transaction is committed. +after_rollback+ callbacks
-
# are called on every record saved or destroyed within a transaction immediately after the
-
# transaction or savepoint is rolled back.
-
#
-
# These callbacks are useful for interacting with other systems since you will be guaranteed
-
# that the callback is only executed when the database is in a permanent state. For example,
-
# +after_commit+ is a good spot to put in a hook to clearing a cache since clearing it from
-
# within a transaction could trigger the cache to be regenerated before the database is updated.
-
#
-
# === Caveats
-
#
-
# If you're on MySQL, then do not use DDL operations in nested transactions
-
# blocks that are emulated with savepoints. That is, do not execute statements
-
# like 'CREATE TABLE' inside such blocks. This is because MySQL automatically
-
# releases all savepoints upon executing a DDL operation. When +transaction+
-
# is finished and tries to release the savepoint it created earlier, a
-
# database error will occur because the savepoint has already been
-
# automatically released. The following example demonstrates the problem:
-
#
-
# Model.connection.transaction do # BEGIN
-
# Model.connection.transaction(:requires_new => true) do # CREATE SAVEPOINT active_record_1
-
# Model.connection.create_table(...) # active_record_1 now automatically released
-
# end # RELEASE savepoint active_record_1
-
# # ^^^^ BOOM! database error!
-
# end
-
#
-
# Note that "TRUNCATE" is also a MySQL DDL statement!
-
1
module ClassMethods
-
# See ActiveRecord::Transactions::ClassMethods for detailed documentation.
-
1
def transaction(options = {}, &block)
-
# See the ConnectionAdapters::DatabaseStatements#transaction API docs.
-
connection.transaction(options, &block)
-
end
-
-
1
def after_commit(*args, &block)
-
options = args.last
-
if options.is_a?(Hash) && options[:on]
-
options[:if] = Array.wrap(options[:if])
-
options[:if] << "transaction_include_action?(:#{options[:on]})"
-
end
-
set_callback(:commit, :after, *args, &block)
-
end
-
-
1
def after_rollback(*args, &block)
-
options = args.last
-
if options.is_a?(Hash) && options[:on]
-
options[:if] = Array.wrap(options[:if])
-
options[:if] << "transaction_include_action?(:#{options[:on]})"
-
end
-
set_callback(:rollback, :after, *args, &block)
-
end
-
end
-
-
# See ActiveRecord::Transactions::ClassMethods for detailed documentation.
-
1
def transaction(options = {}, &block)
-
self.class.transaction(options, &block)
-
end
-
-
1
def destroy #:nodoc:
-
with_transaction_returning_status { super }
-
end
-
-
1
def save(*) #:nodoc:
-
rollback_active_record_state! do
-
with_transaction_returning_status { super }
-
end
-
end
-
-
1
def save!(*) #:nodoc:
-
with_transaction_returning_status { super }
-
end
-
-
# Reset id and @new_record if the transaction rolls back.
-
1
def rollback_active_record_state!
-
remember_transaction_record_state
-
yield
-
rescue Exception
-
IdentityMap.remove(self) if IdentityMap.enabled?
-
restore_transaction_record_state
-
raise
-
ensure
-
clear_transaction_record_state
-
end
-
-
# Call the after_commit callbacks
-
1
def committed! #:nodoc:
-
run_callbacks :commit
-
ensure
-
clear_transaction_record_state
-
end
-
-
# Call the after rollback callbacks. The restore_state argument indicates if the record
-
# state should be rolled back to the beginning or just to the last savepoint.
-
1
def rolledback!(force_restore_state = false) #:nodoc:
-
run_callbacks :rollback
-
ensure
-
IdentityMap.remove(self) if IdentityMap.enabled?
-
restore_transaction_record_state(force_restore_state)
-
end
-
-
# Add the record to the current transaction so that the :after_rollback and :after_commit callbacks
-
# can be called.
-
1
def add_to_transaction
-
if self.class.connection.add_transaction_record(self)
-
remember_transaction_record_state
-
end
-
end
-
-
# Executes +method+ within a transaction and captures its return value as a
-
# status flag. If the status is true the transaction is committed, otherwise
-
# a ROLLBACK is issued. In any case the status flag is returned.
-
#
-
# This method is available within the context of an ActiveRecord::Base
-
# instance.
-
1
def with_transaction_returning_status
-
status = nil
-
self.class.transaction do
-
add_to_transaction
-
status = yield
-
raise ActiveRecord::Rollback unless status
-
end
-
status
-
end
-
-
1
protected
-
-
# Save the new record state and id of a record so it can be restored later if a transaction fails.
-
1
def remember_transaction_record_state #:nodoc
-
@_start_transaction_state ||= {}
-
@_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key)
-
unless @_start_transaction_state.include?(:new_record)
-
@_start_transaction_state[:new_record] = @new_record
-
end
-
unless @_start_transaction_state.include?(:destroyed)
-
@_start_transaction_state[:destroyed] = @destroyed
-
end
-
@_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) + 1
-
end
-
-
# Clear the new record state and id of a record.
-
1
def clear_transaction_record_state #:nodoc
-
if defined?(@_start_transaction_state)
-
@_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
-
remove_instance_variable(:@_start_transaction_state) if @_start_transaction_state[:level] < 1
-
end
-
end
-
-
# Restore the new record state and id of a record that was previously saved by a call to save_record_state.
-
1
def restore_transaction_record_state(force = false) #:nodoc
-
if defined?(@_start_transaction_state)
-
@_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
-
if @_start_transaction_state[:level] < 1
-
restore_state = remove_instance_variable(:@_start_transaction_state)
-
@attributes = @attributes.dup if @attributes.frozen?
-
@new_record = restore_state[:new_record]
-
@destroyed = restore_state[:destroyed]
-
if restore_state.has_key?(:id)
-
self.id = restore_state[:id]
-
else
-
@attributes.delete(self.class.primary_key)
-
@attributes_cache.delete(self.class.primary_key)
-
end
-
end
-
end
-
end
-
-
# Determine if a record was created or destroyed in a transaction. State should be one of :new_record or :destroyed.
-
1
def transaction_record_state(state) #:nodoc
-
@_start_transaction_state[state] if defined?(@_start_transaction_state)
-
end
-
-
# Determine if a transaction included an action for :create, :update, or :destroy. Used in filtering callbacks.
-
1
def transaction_include_action?(action) #:nodoc
-
case action
-
when :create
-
transaction_record_state(:new_record)
-
when :destroy
-
destroyed?
-
when :update
-
!(transaction_record_state(:new_record) || destroyed?)
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
# = Active Record RecordInvalid
-
#
-
# Raised by <tt>save!</tt> and <tt>create!</tt> when the record is invalid. Use the
-
# +record+ method to retrieve the record which did not validate.
-
#
-
# begin
-
# complex_operation_that_calls_save!_internally
-
# rescue ActiveRecord::RecordInvalid => invalid
-
# puts invalid.record.errors
-
# end
-
1
class RecordInvalid < ActiveRecordError
-
1
attr_reader :record
-
1
def initialize(record)
-
@record = record
-
errors = @record.errors.full_messages.join(", ")
-
super(I18n.t("activerecord.errors.messages.record_invalid", :errors => errors))
-
end
-
end
-
-
# = Active Record Validations
-
#
-
# Active Record includes the majority of its validations from <tt>ActiveModel::Validations</tt>
-
# all of which accept the <tt>:on</tt> argument to define the context where the
-
# validations are active. Active Record will always supply either the context of
-
# <tt>:create</tt> or <tt>:update</tt> dependent on whether the model is a
-
# <tt>new_record?</tt>.
-
1
module Validations
-
1
extend ActiveSupport::Concern
-
1
include ActiveModel::Validations
-
-
1
module ClassMethods
-
# Creates an object just like Base.create but calls <tt>save!</tt> instead of +save+
-
# so an exception is raised if the record is invalid.
-
1
def create!(attributes = nil, options = {}, &block)
-
if attributes.is_a?(Array)
-
attributes.collect { |attr| create!(attr, options, &block) }
-
else
-
object = new(attributes, options)
-
yield(object) if block_given?
-
object.save!
-
object
-
end
-
end
-
end
-
-
# The validation process on save can be skipped by passing <tt>:validate => false</tt>. The regular Base#save method is
-
# replaced with this when the validations module is mixed in, which it is by default.
-
1
def save(options={})
-
perform_validations(options) ? super : false
-
end
-
-
# Attempts to save the record just like Base#save but will raise a +RecordInvalid+ exception instead of returning false
-
# if the record is not valid.
-
1
def save!(options={})
-
perform_validations(options) ? super : raise(RecordInvalid.new(self))
-
end
-
-
# Runs all the validations within the specified context. Returns true if no errors are found,
-
# false otherwise.
-
#
-
# If the argument is false (default is +nil+), the context is set to <tt>:create</tt> if
-
# <tt>new_record?</tt> is true, and to <tt>:update</tt> if it is not.
-
#
-
# Validations with no <tt>:on</tt> option will run no matter the context. Validations with
-
# some <tt>:on</tt> option will only run in the specified context.
-
1
def valid?(context = nil)
-
context ||= (new_record? ? :create : :update)
-
output = super(context)
-
errors.empty? && output
-
end
-
-
1
protected
-
-
1
def perform_validations(options={})
-
perform_validation = options[:validate] != false
-
perform_validation ? valid?(options[:context]) : true
-
end
-
end
-
end
-
-
1
require "active_record/validations/associated"
-
1
require "active_record/validations/uniqueness"
-
1
module ActiveRecord
-
1
module Validations
-
1
class AssociatedValidator < ActiveModel::EachValidator
-
1
def validate_each(record, attribute, value)
-
return if (value.is_a?(Array) ? value : [value]).collect{ |r| r.nil? || r.valid? }.all?
-
record.errors.add(attribute, :invalid, options.merge(:value => value))
-
end
-
end
-
-
1
module ClassMethods
-
# Validates whether the associated object or objects are all valid themselves. Works with any kind of association.
-
#
-
# class Book < ActiveRecord::Base
-
# has_many :pages
-
# belongs_to :library
-
#
-
# validates_associated :pages, :library
-
# end
-
#
-
# WARNING: This validation must not be used on both ends of an association. Doing so will lead to a circular dependency and cause infinite recursion.
-
#
-
# NOTE: This validation will not fail if the association hasn't been assigned. If you want to
-
# ensure that the association is both present and guaranteed to be valid, you also need to
-
# use +validates_presence_of+.
-
#
-
# Configuration options:
-
# * <tt>:message</tt> - A custom error message (default is: "is invalid")
-
# * <tt>:on</tt> - Specifies when this validation is active. Runs in all
-
# validation contexts by default (+nil+), other options are <tt>:create</tt>
-
# and <tt>:update</tt>.
-
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
-
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
-
# method, proc or string should return or evaluate to a true or false value.
-
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
-
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
-
# method, proc or string should return or evaluate to a true or false value.
-
1
def validates_associated(*attr_names)
-
1
validates_with AssociatedValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/array/wrap'
-
-
1
module ActiveRecord
-
1
module Validations
-
1
class UniquenessValidator < ActiveModel::EachValidator
-
1
def initialize(options)
-
super(options.reverse_merge(:case_sensitive => true))
-
end
-
-
# Unfortunately, we have to tie Uniqueness validators to a class.
-
1
def setup(klass)
-
@klass = klass
-
end
-
-
1
def validate_each(record, attribute, value)
-
finder_class = find_finder_class_for(record)
-
table = finder_class.arel_table
-
-
coder = record.class.serialized_attributes[attribute.to_s]
-
-
if value && coder
-
value = coder.dump value
-
end
-
-
relation = build_relation(finder_class, table, attribute, value)
-
relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.send(:id))) if record.persisted?
-
-
Array.wrap(options[:scope]).each do |scope_item|
-
scope_value = record.send(scope_item)
-
relation = relation.and(table[scope_item].eq(scope_value))
-
end
-
-
if finder_class.unscoped.where(relation).exists?
-
record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
-
end
-
end
-
-
1
protected
-
-
# The check for an existing value should be run from a class that
-
# isn't abstract. This means working down from the current class
-
# (self), to the first non-abstract class. Since classes don't know
-
# their subclasses, we have to build the hierarchy between self and
-
# the record's class.
-
1
def find_finder_class_for(record) #:nodoc:
-
class_hierarchy = [record.class]
-
-
while class_hierarchy.first != @klass
-
class_hierarchy.insert(0, class_hierarchy.first.superclass)
-
end
-
-
class_hierarchy.detect { |klass| !klass.abstract_class? }
-
end
-
-
1
def build_relation(klass, table, attribute, value) #:nodoc:
-
column = klass.columns_hash[attribute.to_s]
-
value = column.limit ? value.to_s.mb_chars[0, column.limit] : value.to_s if column.text?
-
-
if !options[:case_sensitive] && value && column.text?
-
# will use SQL LOWER function before comparison
-
relation = table[attribute].lower.eq(table.lower(value))
-
else
-
value = klass.connection.case_sensitive_modifier(value)
-
relation = table[attribute].eq(value)
-
end
-
-
relation
-
end
-
end
-
-
1
module ClassMethods
-
# Validates whether the value of the specified attributes are unique across the system.
-
# Useful for making sure that only one user
-
# can be named "davidhh".
-
#
-
# class Person < ActiveRecord::Base
-
# validates_uniqueness_of :user_name
-
# end
-
#
-
# It can also validate whether the value of the specified attributes are unique based on a scope parameter:
-
#
-
# class Person < ActiveRecord::Base
-
# validates_uniqueness_of :user_name, :scope => :account_id
-
# end
-
#
-
# Or even multiple scope parameters. For example, making sure that a teacher can only be on the schedule once
-
# per semester for a particular class.
-
#
-
# class TeacherSchedule < ActiveRecord::Base
-
# validates_uniqueness_of :teacher_id, :scope => [:semester_id, :class_id]
-
# end
-
#
-
# When the record is created, a check is performed to make sure that no record exists in the database
-
# with the given value for the specified attribute (that maps to a column). When the record is updated,
-
# the same check is made but disregarding the record itself.
-
#
-
# Configuration options:
-
# * <tt>:message</tt> - Specifies a custom error message (default is: "has already been taken").
-
# * <tt>:scope</tt> - One or more columns by which to limit the scope of the uniqueness constraint.
-
# * <tt>:case_sensitive</tt> - Looks for an exact match. Ignored by non-text columns (+true+ by default).
-
# * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
-
# * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
-
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
-
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>).
-
# The method, proc or string should return or evaluate to a true or false value.
-
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
-
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or
-
# <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The method, proc or string should
-
# return or evaluate to a true or false value.
-
#
-
# === Concurrency and integrity
-
#
-
# Using this validation method in conjunction with ActiveRecord::Base#save
-
# does not guarantee the absence of duplicate record insertions, because
-
# uniqueness checks on the application level are inherently prone to race
-
# conditions. For example, suppose that two users try to post a Comment at
-
# the same time, and a Comment's title must be unique. At the database-level,
-
# the actions performed by these users could be interleaved in the following manner:
-
#
-
# User 1 | User 2
-
# ------------------------------------+--------------------------------------
-
# # User 1 checks whether there's |
-
# # already a comment with the title |
-
# # 'My Post'. This is not the case. |
-
# SELECT * FROM comments |
-
# WHERE title = 'My Post' |
-
# |
-
# | # User 2 does the same thing and also
-
# | # infers that his title is unique.
-
# | SELECT * FROM comments
-
# | WHERE title = 'My Post'
-
# |
-
# # User 1 inserts his comment. |
-
# INSERT INTO comments |
-
# (title, content) VALUES |
-
# ('My Post', 'hi!') |
-
# |
-
# | # User 2 does the same thing.
-
# | INSERT INTO comments
-
# | (title, content) VALUES
-
# | ('My Post', 'hello!')
-
# |
-
# | # ^^^^^^
-
# | # Boom! We now have a duplicate
-
# | # title!
-
#
-
# This could even happen if you use transactions with the 'serializable'
-
# isolation level. The best way to work around this problem is to add a unique
-
# index to the database table using
-
# ActiveRecord::ConnectionAdapters::SchemaStatements#add_index. In the
-
# rare case that a race condition occurs, the database will guarantee
-
# the field's uniqueness.
-
#
-
# When the database catches such a duplicate insertion,
-
# ActiveRecord::Base#save will raise an ActiveRecord::StatementInvalid
-
# exception. You can either choose to let this error propagate (which
-
# will result in the default Rails exception page being shown), or you
-
# can catch it and restart the transaction (e.g. by telling the user
-
# that the title already exists, and asking him to re-enter the title).
-
# This technique is also known as optimistic concurrency control:
-
# http://en.wikipedia.org/wiki/Optimistic_concurrency_control
-
#
-
# The bundled ActiveRecord::ConnectionAdapters distinguish unique index
-
# constraint errors from other types of database errors by throwing an
-
# ActiveRecord::RecordNotUnique exception.
-
# For other adapters you will have to parse the (database-specific) exception
-
# message to detect such a case.
-
# The following bundled adapters throw the ActiveRecord::RecordNotUnique exception:
-
# * ActiveRecord::ConnectionAdapters::MysqlAdapter
-
# * ActiveRecord::ConnectionAdapters::Mysql2Adapter
-
# * ActiveRecord::ConnectionAdapters::SQLiteAdapter
-
# * ActiveRecord::ConnectionAdapters::SQLite3Adapter
-
# * ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
-
#
-
1
def validates_uniqueness_of(*attr_names)
-
validates_with UniquenessValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module VERSION #:nodoc:
-
1
MAJOR = 3
-
1
MINOR = 1
-
1
TINY = 0
-
1
PRE = nil
-
-
1
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
-
end
-
end
-
#--
-
# Copyright (c) 2006-2011 David Heinemeier Hansson
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining
-
# a copy of this software and associated documentation files (the
-
# "Software"), to deal in the Software without restriction, including
-
# without limitation the rights to use, copy, modify, merge, publish,
-
# distribute, sublicense, and/or sell copies of the Software, and to
-
# permit persons to whom the Software is furnished to do so, subject to
-
# the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be
-
# included in all copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
#++
-
-
1
activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
-
1
$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
-
-
1
activemodel_path = File.expand_path('../../../activemodel/lib', __FILE__)
-
1
$:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include?(activemodel_path)
-
-
1
require 'active_support'
-
1
require 'active_model'
-
1
require 'active_resource/version'
-
-
1
module ActiveResource
-
1
extend ActiveSupport::Autoload
-
-
1
autoload :Base
-
1
autoload :Connection
-
1
autoload :CustomMethods
-
1
autoload :Formats
-
1
autoload :HttpMock
-
1
autoload :Observing
-
1
autoload :Schema
-
1
autoload :Validations
-
end
-
1
require "active_resource"
-
1
require "rails"
-
-
1
module ActiveResource
-
1
class Railtie < Rails::Railtie
-
1
config.active_resource = ActiveSupport::OrderedOptions.new
-
-
1
initializer "active_resource.set_configs" do |app|
-
1
app.config.active_resource.each do |k,v|
-
ActiveResource::Base.send "#{k}=", v
-
end
-
end
-
end
-
end
-
1
module ActiveResource
-
1
module VERSION #:nodoc:
-
1
MAJOR = 3
-
1
MINOR = 1
-
1
TINY = 0
-
1
PRE = nil
-
-
1
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
-
end
-
end
-
1
require 'active_support'
-
1
require 'active_support/time'
-
1
require 'active_support/core_ext'
-
1
begin
-
1
require 'base64'
-
rescue LoadError
-
end
-
-
1
module ActiveSupport
-
1
if defined? ::Base64
-
1
Base64 = ::Base64
-
else
-
# Base64 provides utility methods for encoding and de-coding binary data
-
# using a base 64 representation. A base 64 representation of binary data
-
# consists entirely of printable US-ASCII characters. The Base64 module
-
# is included in Ruby 1.8, but has been removed in Ruby 1.9.
-
module Base64
-
# Encodes a string to its base 64 representation. Each 60 characters of
-
# output is separated by a newline character.
-
#
-
# ActiveSupport::Base64.encode64("Original unencoded string")
-
# # => "T3JpZ2luYWwgdW5lbmNvZGVkIHN0cmluZw==\n"
-
def self.encode64(data)
-
[data].pack("m")
-
end
-
-
# Decodes a base 64 encoded string to its original representation.
-
#
-
# ActiveSupport::Base64.decode64("T3JpZ2luYWwgdW5lbmNvZGVkIHN0cmluZw==")
-
# # => "Original unencoded string"
-
def self.decode64(data)
-
data.unpack("m").first
-
end
-
end
-
end
-
-
# Encodes the value as base64 without the newline breaks. This makes the base64 encoding readily usable as URL parameters
-
# or memcache keys without further processing.
-
#
-
# ActiveSupport::Base64.encode64s("Original unencoded string")
-
# # => "T3JpZ2luYWwgdW5lbmNvZGVkIHN0cmluZw=="
-
1
def Base64.encode64s(value)
-
encode64(value).gsub(/\n/, '')
-
end
-
end
-
1
module ActiveSupport
-
1
if defined? ::BasicObject
-
# A class with no predefined methods that behaves similarly to Builder's
-
# BlankSlate. Used for proxy classes.
-
1
class BasicObject < ::BasicObject
-
1
undef_method :==
-
1
undef_method :equal?
-
-
# Let ActiveSupport::BasicObject at least raise exceptions.
-
1
def raise(*args)
-
::Object.send(:raise, *args)
-
end
-
end
-
else
-
class BasicObject #:nodoc:
-
instance_methods.each do |m|
-
undef_method(m) if m.to_s !~ /(?:^__|^nil\?$|^send$|^object_id$)/
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/benchmark'
-
1
require 'active_support/core_ext/hash/keys'
-
-
1
module ActiveSupport
-
1
module Benchmarkable
-
# Allows you to measure the execution time of a block
-
# in a template and records the result to the log. Wrap this block around
-
# expensive operations or possible bottlenecks to get a time reading
-
# for the operation. For example, let's say you thought your file
-
# processing method was taking too long; you could wrap it in a benchmark block.
-
#
-
# <% benchmark "Process data files" do %>
-
# <%= expensive_files_operation %>
-
# <% end %>
-
#
-
# That would add something like "Process data files (345.2ms)" to the log,
-
# which you can then use to compare timings when optimizing your code.
-
#
-
# You may give an optional logger level as the :level option.
-
# (:debug, :info, :warn, :error); the default value is :info.
-
#
-
# <% benchmark "Low-level files", :level => :debug do %>
-
# <%= lowlevel_files_operation %>
-
# <% end %>
-
#
-
# Finally, you can pass true as the third argument to silence all log activity
-
# inside the block. This is great for boiling down a noisy block to just a single statement:
-
#
-
# <% benchmark "Process data files", :level => :info, :silence => true do %>
-
# <%= expensive_and_chatty_files_operation %>
-
# <% end %>
-
1
def benchmark(message = "Benchmarking", options = {})
-
if logger
-
if options.is_a?(Symbol)
-
ActiveSupport::Deprecation.warn("use benchmark('#{message}', :level => :#{options}) instead", caller)
-
options = { :level => options, :silence => false }
-
else
-
options.assert_valid_keys(:level, :silence)
-
options[:level] ||= :info
-
end
-
-
result = nil
-
ms = Benchmark.ms { result = options[:silence] ? logger.silence { yield } : yield }
-
logger.send(options[:level], '%s (%.1fms)' % [ message, ms ])
-
result
-
else
-
yield
-
end
-
end
-
-
# Silence the logger during the execution of the block.
-
#
-
1
def silence
-
old_logger_level, logger.level = logger.level, ::Logger::ERROR if logger
-
yield
-
ensure
-
logger.level = old_logger_level if logger
-
end
-
end
-
end
-
1
require 'thread'
-
1
require 'active_support/core_ext/class/attribute_accessors'
-
-
1
module ActiveSupport
-
# Inspired by the buffered logger idea by Ezra
-
1
class BufferedLogger
-
1
module Severity
-
1
DEBUG = 0
-
1
INFO = 1
-
1
WARN = 2
-
1
ERROR = 3
-
1
FATAL = 4
-
1
UNKNOWN = 5
-
end
-
1
include Severity
-
-
1
MAX_BUFFER_SIZE = 1000
-
-
##
-
# :singleton-method:
-
# Set to false to disable the silencer
-
1
cattr_accessor :silencer
-
1
self.silencer = true
-
-
# Silences the logger for the duration of the block.
-
1
def silence(temporary_level = ERROR)
-
if silencer
-
begin
-
old_logger_level, self.level = level, temporary_level
-
yield self
-
ensure
-
self.level = old_logger_level
-
end
-
else
-
yield self
-
end
-
end
-
-
1
attr_accessor :level
-
1
attr_reader :auto_flushing
-
-
1
def initialize(log, level = DEBUG)
-
1
@level = level
-
5
@buffer = Hash.new { |h,k| h[k] = [] }
-
1
@auto_flushing = 1
-
1
@guard = Mutex.new
-
-
1
if log.respond_to?(:write)
-
@log = log
-
1
elsif File.exist?(log)
-
1
@log = open_log(log, (File::WRONLY | File::APPEND))
-
else
-
FileUtils.mkdir_p(File.dirname(log))
-
@log = open_log(log, (File::WRONLY | File::APPEND | File::CREAT))
-
end
-
end
-
-
1
def open_log(log, mode)
-
1
open(log, mode).tap do |open_log|
-
1
open_log.set_encoding(Encoding::BINARY) if open_log.respond_to?(:set_encoding)
-
1
open_log.sync = true
-
end
-
end
-
-
1
def add(severity, message = nil, progname = nil, &block)
-
3
return if @level > severity
-
3
message = (message || (block && block.call) || progname).to_s
-
# If a newline is necessary then create a new message ending with a newline.
-
# Ensures that the original message is not mutated.
-
3
message = "#{message}\n" unless message[-1] == ?\n
-
3
buffer << message
-
3
auto_flush
-
3
message
-
end
-
-
# Dynamically add methods such as:
-
# def info
-
# def warn
-
# def debug
-
1
for severity in Severity.constants
-
6
class_eval <<-EOT, __FILE__, __LINE__ + 1
-
def #{severity.downcase}(message = nil, progname = nil, &block) # def debug(message = nil, progname = nil, &block)
-
add(#{severity}, message, progname, &block) # add(DEBUG, message, progname, &block)
-
end # end
-
-
def #{severity.downcase}? # def debug?
-
#{severity} >= @level # DEBUG >= @level
-
end # end
-
EOT
-
end
-
-
# Set the auto-flush period. Set to true to flush after every log message,
-
# to an integer to flush every N messages, or to false, nil, or zero to
-
# never auto-flush. If you turn auto-flushing off, be sure to regularly
-
# flush the log yourself -- it will eat up memory until you do.
-
1
def auto_flushing=(period)
-
@auto_flushing =
-
case period
-
when true; 1
-
when false, nil, 0; MAX_BUFFER_SIZE
-
when Integer; period
-
else raise ArgumentError, "Unrecognized auto_flushing period: #{period.inspect}"
-
end
-
end
-
-
1
def flush
-
4
@guard.synchronize do
-
4
buffer.each do |content|
-
3
@log.write(content)
-
end
-
-
# Important to do this even if buffer was empty or else @buffer will
-
# accumulate empty arrays for each request where nothing was logged.
-
4
clear_buffer
-
end
-
end
-
-
1
def close
-
flush
-
@log.close if @log.respond_to?(:close)
-
@log = nil
-
end
-
-
1
protected
-
1
def auto_flush
-
3
flush if buffer.size >= @auto_flushing
-
end
-
-
1
def buffer
-
10
@buffer[Thread.current]
-
end
-
-
1
def clear_buffer
-
4
@buffer.delete(Thread.current)
-
end
-
end
-
end
-
1
require 'benchmark'
-
1
require 'zlib'
-
1
require 'active_support/core_ext/array/extract_options'
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_support/core_ext/benchmark'
-
1
require 'active_support/core_ext/exception'
-
1
require 'active_support/core_ext/class/attribute_accessors'
-
1
require 'active_support/core_ext/numeric/bytes'
-
1
require 'active_support/core_ext/numeric/time'
-
1
require 'active_support/core_ext/object/to_param'
-
1
require 'active_support/core_ext/string/inflections'
-
-
1
module ActiveSupport
-
# See ActiveSupport::Cache::Store for documentation.
-
1
module Cache
-
1
autoload :FileStore, 'active_support/cache/file_store'
-
1
autoload :MemoryStore, 'active_support/cache/memory_store'
-
1
autoload :MemCacheStore, 'active_support/cache/mem_cache_store'
-
1
autoload :SynchronizedMemoryStore, 'active_support/cache/synchronized_memory_store'
-
1
autoload :CompressedMemCacheStore, 'active_support/cache/compressed_mem_cache_store'
-
-
# These options mean something to all cache implementations. Individual cache
-
# implementations may support additional options.
-
1
UNIVERSAL_OPTIONS = [:namespace, :compress, :compress_threshold, :expires_in, :race_condition_ttl]
-
-
1
module Strategy
-
1
autoload :LocalCache, 'active_support/cache/strategy/local_cache'
-
end
-
-
# Creates a new CacheStore object according to the given options.
-
#
-
# If no arguments are passed to this method, then a new
-
# ActiveSupport::Cache::MemoryStore object will be returned.
-
#
-
# If you pass a Symbol as the first argument, then a corresponding cache
-
# store class under the ActiveSupport::Cache namespace will be created.
-
# For example:
-
#
-
# ActiveSupport::Cache.lookup_store(:memory_store)
-
# # => returns a new ActiveSupport::Cache::MemoryStore object
-
#
-
# ActiveSupport::Cache.lookup_store(:mem_cache_store)
-
# # => returns a new ActiveSupport::Cache::MemCacheStore object
-
#
-
# Any additional arguments will be passed to the corresponding cache store
-
# class's constructor:
-
#
-
# ActiveSupport::Cache.lookup_store(:file_store, "/tmp/cache")
-
# # => same as: ActiveSupport::Cache::FileStore.new("/tmp/cache")
-
#
-
# If the first argument is not a Symbol, then it will simply be returned:
-
#
-
# ActiveSupport::Cache.lookup_store(MyOwnCacheStore.new)
-
# # => returns MyOwnCacheStore.new
-
1
def self.lookup_store(*store_option)
-
3
store, *parameters = *Array.wrap(store_option).flatten
-
-
3
case store
-
when Symbol
-
2
store_class_name = store.to_s.camelize
-
2
store_class =
-
begin
-
2
require "active_support/cache/#{store}"
-
rescue LoadError => e
-
raise "Could not find cache store adapter for #{store} (#{e})"
-
else
-
2
ActiveSupport::Cache.const_get(store_class_name)
-
end
-
2
store_class.new(*parameters)
-
when nil
-
ActiveSupport::Cache::MemoryStore.new
-
else
-
1
store
-
end
-
end
-
-
1
def self.expand_cache_key(key, namespace = nil)
-
expanded_cache_key = namespace ? "#{namespace}/" : ""
-
-
prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]
-
if prefix
-
expanded_cache_key << "#{prefix}/"
-
end
-
-
expanded_cache_key <<
-
if key.respond_to?(:cache_key)
-
key.cache_key
-
elsif key.is_a?(Array)
-
if key.size > 1
-
key.collect { |element| expand_cache_key(element) }.to_param
-
else
-
key.first.to_param
-
end
-
elsif key
-
key.to_param
-
end.to_s
-
-
expanded_cache_key
-
end
-
-
# An abstract cache store class. There are multiple cache store
-
# implementations, each having its own additional features. See the classes
-
# under the ActiveSupport::Cache module, e.g.
-
# ActiveSupport::Cache::MemCacheStore. MemCacheStore is currently the most
-
# popular cache store for large production websites.
-
#
-
# Some implementations may not support all methods beyond the basic cache
-
# methods of +fetch+, +write+, +read+, +exist?+, and +delete+.
-
#
-
# ActiveSupport::Cache::Store can store any serializable Ruby object.
-
#
-
# cache = ActiveSupport::Cache::MemoryStore.new
-
#
-
# cache.read("city") # => nil
-
# cache.write("city", "Duckburgh")
-
# cache.read("city") # => "Duckburgh"
-
#
-
# Keys are always translated into Strings and are case sensitive. When an
-
# object is specified as a key, its +cache_key+ method will be called if it
-
# is defined. Otherwise, the +to_param+ method will be called. Hashes and
-
# Arrays can be used as keys. The elements will be delimited by slashes
-
# and Hashes elements will be sorted by key so they are consistent.
-
#
-
# cache.read("city") == cache.read(:city) # => true
-
#
-
# Nil values can be cached.
-
#
-
# If your cache is on a shared infrastructure, you can define a namespace for
-
# your cache entries. If a namespace is defined, it will be prefixed on to every
-
# key. The namespace can be either a static value or a Proc. If it is a Proc, it
-
# will be invoked when each key is evaluated so that you can use application logic
-
# to invalidate keys.
-
#
-
# cache.namespace = lambda { @last_mod_time } # Set the namespace to a variable
-
# @last_mod_time = Time.now # Invalidate the entire cache by changing namespace
-
#
-
#
-
# Caches can also store values in a compressed format to save space and reduce
-
# time spent sending data. Since there is some overhead, values must be large
-
# enough to warrant compression. To turn on compression either pass
-
# <tt>:compress => true</tt> in the initializer or to +fetch+ or +write+.
-
# To specify the threshold at which to compress values, set
-
# <tt>:compress_threshold</tt>. The default threshold is 32K.
-
1
class Store
-
-
1
cattr_accessor :logger, :instance_writer => true
-
-
1
attr_reader :silence, :options
-
1
alias :silence? :silence
-
-
# Create a new cache. The options will be passed to any write method calls except
-
# for :namespace which can be used to set the global namespace for the cache.
-
1
def initialize (options = nil)
-
2
@options = options ? options.dup : {}
-
end
-
-
# Silence the logger.
-
1
def silence!
-
@silence = true
-
self
-
end
-
-
# Silence the logger within a block.
-
1
def mute
-
previous_silence, @silence = defined?(@silence) && @silence, true
-
yield
-
ensure
-
@silence = previous_silence
-
end
-
-
# Set to true if cache stores should be instrumented. Default is false.
-
1
def self.instrument=(boolean)
-
Thread.current[:instrument_cache_store] = boolean
-
end
-
-
1
def self.instrument
-
Thread.current[:instrument_cache_store] || false
-
end
-
-
# Fetches data from the cache, using the given key. If there is data in
-
# the cache with the given key, then that data is returned.
-
#
-
# If there is no such data in the cache (a cache miss occurred),
-
# then nil will be returned. However, if a block has been passed, then
-
# that block will be run in the event of a cache miss. The return value
-
# of the block will be written to the cache under the given cache key,
-
# and that return value will be returned.
-
#
-
# cache.write("today", "Monday")
-
# cache.fetch("today") # => "Monday"
-
#
-
# cache.fetch("city") # => nil
-
# cache.fetch("city") do
-
# "Duckburgh"
-
# end
-
# cache.fetch("city") # => "Duckburgh"
-
#
-
# You may also specify additional options via the +options+ argument.
-
# Setting <tt>:force => true</tt> will force a cache miss:
-
#
-
# cache.write("today", "Monday")
-
# cache.fetch("today", :force => true) # => nil
-
#
-
# Setting <tt>:compress</tt> will store a large cache entry set by the call
-
# in a compressed format.
-
#
-
#
-
# Setting <tt>:expires_in</tt> will set an expiration time on the cache. All caches
-
# support auto expiring content after a specified number of seconds. This value can
-
# be specified as an option to the construction in which call all entries will be
-
# affected. Or it can be supplied to the +fetch+ or +write+ method for just one entry.
-
#
-
# cache = ActiveSupport::Cache::MemoryStore.new(:expires_in => 5.minutes)
-
# cache.write(key, value, :expires_in => 1.minute) # Set a lower value for one entry
-
#
-
# Setting <tt>:race_condition_ttl</tt> is very useful in situations where a cache entry
-
# is used very frequently and is under heavy load. If a cache expires and due to heavy load
-
# seven different processes will try to read data natively and then they all will try to
-
# write to cache. To avoid that case the first process to find an expired cache entry will
-
# bump the cache expiration time by the value set in <tt>:race_condition_ttl</tt>. Yes
-
# this process is extending the time for a stale value by another few seconds. Because
-
# of extended life of the previous cache, other processes will continue to use slightly
-
# stale data for a just a big longer. In the meantime that first process will go ahead
-
# and will write into cache the new value. After that all the processes will start
-
# getting new value. The key is to keep <tt>:race_condition_ttl</tt> small.
-
#
-
# If the process regenerating the entry errors out, the entry will be regenerated
-
# after the specified number of seconds. Also note that the life of stale cache is
-
# extended only if it expired recently. Otherwise a new value is generated and
-
# <tt>:race_condition_ttl</tt> does not play any role.
-
#
-
# # Set all values to expire after one minute.
-
# cache = ActiveSupport::Cache::MemoryCache.new(:expires_in => 1.minute)
-
#
-
# cache.write("foo", "original value")
-
# val_1 = nil
-
# val_2 = nil
-
# sleep 60
-
#
-
# Thread.new do
-
# val_1 = cache.fetch("foo", :race_condition_ttl => 10) do
-
# sleep 1
-
# "new value 1"
-
# end
-
# end
-
#
-
# Thread.new do
-
# val_2 = cache.fetch("foo", :race_condition_ttl => 10) do
-
# "new value 2"
-
# end
-
# end
-
#
-
# # val_1 => "new value 1"
-
# # val_2 => "original value"
-
# # sleep 10 # First thread extend the life of cache by another 10 seconds
-
# # cache.fetch("foo") => "new value 1"
-
#
-
# Other options will be handled by the specific cache store implementation.
-
# Internally, #fetch calls #read_entry, and calls #write_entry on a cache miss.
-
# +options+ will be passed to the #read and #write calls.
-
#
-
# For example, MemCacheStore's #write method supports the +:raw+
-
# option, which tells the memcached server to store all values as strings.
-
# We can use this option with #fetch too:
-
#
-
# cache = ActiveSupport::Cache::MemCacheStore.new
-
# cache.fetch("foo", :force => true, :raw => true) do
-
# :bar
-
# end
-
# cache.fetch("foo") # => "bar"
-
1
def fetch(name, options = nil)
-
if block_given?
-
options = merged_options(options)
-
key = namespaced_key(name, options)
-
unless options[:force]
-
entry = instrument(:read, name, options) do |payload|
-
payload[:super_operation] = :fetch if payload
-
read_entry(key, options)
-
end
-
end
-
if entry && entry.expired?
-
race_ttl = options[:race_condition_ttl].to_f
-
if race_ttl and Time.now.to_f - entry.expires_at <= race_ttl
-
entry.expires_at = Time.now + race_ttl
-
write_entry(key, entry, :expires_in => race_ttl * 2)
-
else
-
delete_entry(key, options)
-
end
-
entry = nil
-
end
-
-
if entry
-
instrument(:fetch_hit, name, options) { |payload| }
-
entry.value
-
else
-
result = instrument(:generate, name, options) do |payload|
-
yield
-
end
-
write(name, result, options)
-
result
-
end
-
else
-
read(name, options)
-
end
-
end
-
-
# Fetches data from the cache, using the given key. If there is data in
-
# the cache with the given key, then that data is returned. Otherwise,
-
# nil is returned.
-
#
-
# Options are passed to the underlying cache implementation.
-
1
def read(name, options = nil)
-
options = merged_options(options)
-
key = namespaced_key(name, options)
-
instrument(:read, name, options) do |payload|
-
entry = read_entry(key, options)
-
if entry
-
if entry.expired?
-
delete_entry(key, options)
-
payload[:hit] = false if payload
-
nil
-
else
-
payload[:hit] = true if payload
-
entry.value
-
end
-
else
-
payload[:hit] = false if payload
-
nil
-
end
-
end
-
end
-
-
# Read multiple values at once from the cache. Options can be passed
-
# in the last argument.
-
#
-
# Some cache implementation may optimize this method.
-
#
-
# Returns a hash mapping the names provided to the values found.
-
1
def read_multi(*names)
-
options = names.extract_options!
-
options = merged_options(options)
-
results = {}
-
names.each do |name|
-
key = namespaced_key(name, options)
-
entry = read_entry(key, options)
-
if entry
-
if entry.expired?
-
delete_entry(key, options)
-
else
-
results[name] = entry.value
-
end
-
end
-
end
-
results
-
end
-
-
# Writes the value to the cache, with the key.
-
#
-
# Options are passed to the underlying cache implementation.
-
1
def write(name, value, options = nil)
-
options = merged_options(options)
-
instrument(:write, name, options) do |payload|
-
entry = Entry.new(value, options)
-
write_entry(namespaced_key(name, options), entry, options)
-
end
-
end
-
-
# Deletes an entry in the cache. Returns +true+ if an entry is deleted.
-
#
-
# Options are passed to the underlying cache implementation.
-
1
def delete(name, options = nil)
-
options = merged_options(options)
-
instrument(:delete, name) do |payload|
-
delete_entry(namespaced_key(name, options), options)
-
end
-
end
-
-
# Return true if the cache contains an entry for the given key.
-
#
-
# Options are passed to the underlying cache implementation.
-
1
def exist?(name, options = nil)
-
options = merged_options(options)
-
instrument(:exist?, name) do |payload|
-
entry = read_entry(namespaced_key(name, options), options)
-
if entry && !entry.expired?
-
true
-
else
-
false
-
end
-
end
-
end
-
-
# Delete all entries with keys matching the pattern.
-
#
-
# Options are passed to the underlying cache implementation.
-
#
-
# All implementations may not support this method.
-
1
def delete_matched(matcher, options = nil)
-
raise NotImplementedError.new("#{self.class.name} does not support delete_matched")
-
end
-
-
# Increment an integer value in the cache.
-
#
-
# Options are passed to the underlying cache implementation.
-
#
-
# All implementations may not support this method.
-
1
def increment(name, amount = 1, options = nil)
-
raise NotImplementedError.new("#{self.class.name} does not support increment")
-
end
-
-
# Increment an integer value in the cache.
-
#
-
# Options are passed to the underlying cache implementation.
-
#
-
# All implementations may not support this method.
-
1
def decrement(name, amount = 1, options = nil)
-
raise NotImplementedError.new("#{self.class.name} does not support decrement")
-
end
-
-
# Cleanup the cache by removing expired entries.
-
#
-
# Options are passed to the underlying cache implementation.
-
#
-
# All implementations may not support this method.
-
1
def cleanup(options = nil)
-
raise NotImplementedError.new("#{self.class.name} does not support cleanup")
-
end
-
-
# Clear the entire cache. Be careful with this method since it could
-
# affect other processes if shared cache is being used.
-
#
-
# Options are passed to the underlying cache implementation.
-
#
-
# All implementations may not support this method.
-
1
def clear(options = nil)
-
raise NotImplementedError.new("#{self.class.name} does not support clear")
-
end
-
-
1
protected
-
# Add the namespace defined in the options to a pattern designed to match keys.
-
# Implementations that support delete_matched should call this method to translate
-
# a pattern that matches names into one that matches namespaced keys.
-
1
def key_matcher(pattern, options)
-
prefix = options[:namespace].is_a?(Proc) ? options[:namespace].call : options[:namespace]
-
if prefix
-
source = pattern.source
-
if source.start_with?('^')
-
source = source[1, source.length]
-
else
-
source = ".*#{source[0, source.length]}"
-
end
-
Regexp.new("^#{Regexp.escape(prefix)}:#{source}", pattern.options)
-
else
-
pattern
-
end
-
end
-
-
# Read an entry from the cache implementation. Subclasses must implement this method.
-
1
def read_entry(key, options) # :nodoc:
-
raise NotImplementedError.new
-
end
-
-
# Write an entry to the cache implementation. Subclasses must implement this method.
-
1
def write_entry(key, entry, options) # :nodoc:
-
raise NotImplementedError.new
-
end
-
-
# Delete an entry from the cache implementation. Subclasses must implement this method.
-
1
def delete_entry(key, options) # :nodoc:
-
raise NotImplementedError.new
-
end
-
-
1
private
-
# Merge the default options with ones specific to a method call.
-
1
def merged_options(call_options) # :nodoc:
-
if call_options
-
options.merge(call_options)
-
else
-
options.dup
-
end
-
end
-
-
# Expand key to be a consistent string value. Invoke +cache_key+ if
-
# object responds to +cache_key+. Otherwise, to_param method will be
-
# called. If the key is a Hash, then keys will be sorted alphabetically.
-
1
def expanded_key(key) # :nodoc:
-
return key.cache_key.to_s if key.respond_to?(:cache_key)
-
-
case key
-
when Array
-
if key.size > 1
-
key = key.collect{|element| expanded_key(element)}
-
else
-
key = key.first
-
end
-
when Hash
-
key = key.sort_by { |k,_| k.to_s }.collect{|k,v| "#{k}=#{v}"}
-
end
-
-
key.to_param
-
end
-
-
# Prefix a key with the namespace. Namespace and key will be delimited with a colon.
-
1
def namespaced_key(key, options)
-
key = expanded_key(key)
-
namespace = options[:namespace] if options
-
prefix = namespace.is_a?(Proc) ? namespace.call : namespace
-
key = "#{prefix}:#{key}" if prefix
-
key
-
end
-
-
1
def instrument(operation, key, options = nil)
-
log(operation, key, options)
-
-
if self.class.instrument
-
payload = { :key => key }
-
payload.merge!(options) if options.is_a?(Hash)
-
ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload){ yield(payload) }
-
else
-
yield(nil)
-
end
-
end
-
-
1
def log(operation, key, options = nil)
-
return unless logger && logger.debug? && !silence?
-
logger.debug("Cache #{operation}: #{key}#{options.blank? ? "" : " (#{options.inspect})"}")
-
end
-
end
-
-
# Entry that is put into caches. It supports expiration time on entries and can compress values
-
# to save space in the cache.
-
1
class Entry
-
1
attr_reader :created_at, :expires_in
-
-
1
DEFAULT_COMPRESS_LIMIT = 16.kilobytes
-
-
1
class << self
-
# Create an entry with internal attributes set. This method is intended to be
-
# used by implementations that store cache entries in a native format instead
-
# of as serialized Ruby objects.
-
1
def create (raw_value, created_at, options = {})
-
entry = new(nil)
-
entry.instance_variable_set(:@value, raw_value)
-
entry.instance_variable_set(:@created_at, created_at.to_f)
-
entry.instance_variable_set(:@compressed, !!options[:compressed])
-
entry.instance_variable_set(:@expires_in, options[:expires_in])
-
entry
-
end
-
end
-
-
# Create a new cache entry for the specified value. Options supported are
-
# +:compress+, +:compress_threshold+, and +:expires_in+.
-
1
def initialize(value, options = {})
-
@compressed = false
-
@expires_in = options[:expires_in]
-
@expires_in = @expires_in.to_f if @expires_in
-
@created_at = Time.now.to_f
-
if value
-
if should_compress?(value, options)
-
@value = Zlib::Deflate.deflate(Marshal.dump(value))
-
@compressed = true
-
else
-
@value = value
-
end
-
else
-
@value = nil
-
end
-
end
-
-
# Get the raw value. This value may be serialized and compressed.
-
1
def raw_value
-
@value
-
end
-
-
# Get the value stored in the cache.
-
1
def value
-
if @value
-
val = compressed? ? Marshal.load(Zlib::Inflate.inflate(@value)) : @value
-
unless val.frozen?
-
val.freeze rescue nil
-
end
-
val
-
end
-
end
-
-
1
def compressed?
-
@compressed
-
end
-
-
# Check if the entry is expired. The +expires_in+ parameter can override the
-
# value set when the entry was created.
-
1
def expired?
-
@expires_in && @created_at + @expires_in <= Time.now.to_f
-
end
-
-
# Set a new time when the entry will expire.
-
1
def expires_at=(time)
-
if time
-
@expires_in = time.to_f - @created_at
-
else
-
@expires_in = nil
-
end
-
end
-
-
# Seconds since the epoch when the entry will expire.
-
1
def expires_at
-
@expires_in ? @created_at + @expires_in : nil
-
end
-
-
# Returns the size of the cached value. This could be less than value.size
-
# if the data is compressed.
-
1
def size
-
if @value.nil?
-
0
-
elsif @value.respond_to?(:bytesize)
-
@value.bytesize
-
else
-
Marshal.dump(@value).bytesize
-
end
-
end
-
-
1
private
-
1
def should_compress?(value, options)
-
if options[:compress] && value
-
unless value.is_a?(Numeric)
-
compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT
-
serialized_value = value.is_a?(String) ? value : Marshal.dump(value)
-
return true if serialized_value.size >= compress_threshold
-
end
-
end
-
false
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/file/atomic'
-
1
require 'active_support/core_ext/string/conversions'
-
1
require 'active_support/core_ext/object/inclusion'
-
1
require 'rack/utils'
-
-
1
module ActiveSupport
-
1
module Cache
-
# A cache store implementation which stores everything on the filesystem.
-
#
-
# FileStore implements the Strategy::LocalCache strategy which implements
-
# an in memory cache inside of a block.
-
1
class FileStore < Store
-
1
attr_reader :cache_path
-
-
1
DIR_FORMATTER = "%03X"
-
-
1
def initialize(cache_path, options = nil)
-
2
super(options)
-
2
@cache_path = cache_path.to_s
-
2
extend Strategy::LocalCache
-
end
-
-
1
def clear(options = nil)
-
root_dirs = Dir.entries(cache_path).reject{|f| f.in?(['.', '..'])}
-
FileUtils.rm_r(root_dirs.collect{|f| File.join(cache_path, f)})
-
end
-
-
1
def cleanup(options = nil)
-
options = merged_options(options)
-
each_key(options) do |key|
-
entry = read_entry(key, options)
-
delete_entry(key, options) if entry && entry.expired?
-
end
-
end
-
-
1
def increment(name, amount = 1, options = nil)
-
file_name = key_file_path(namespaced_key(name, options))
-
lock_file(file_name) do
-
options = merged_options(options)
-
if num = read(name, options)
-
num = num.to_i + amount
-
write(name, num, options)
-
num
-
else
-
nil
-
end
-
end
-
end
-
-
1
def decrement(name, amount = 1, options = nil)
-
file_name = key_file_path(namespaced_key(name, options))
-
lock_file(file_name) do
-
options = merged_options(options)
-
if num = read(name, options)
-
num = num.to_i - amount
-
write(name, num, options)
-
num
-
else
-
nil
-
end
-
end
-
end
-
-
1
def delete_matched(matcher, options = nil)
-
options = merged_options(options)
-
instrument(:delete_matched, matcher.inspect) do
-
matcher = key_matcher(matcher, options)
-
search_dir(cache_path) do |path|
-
key = file_path_key(path)
-
delete_entry(key, options) if key.match(matcher)
-
end
-
end
-
end
-
-
1
protected
-
-
1
def read_entry(key, options)
-
file_name = key_file_path(key)
-
if File.exist?(file_name)
-
entry = File.open(file_name) { |f| Marshal.load(f) }
-
if entry && !entry.expired? && !entry.expires_in && !self.options[:expires_in]
-
# Check for deprecated use of +:expires_in+ option from versions < 3.0
-
deprecated_expires_in = options[:expires_in]
-
if deprecated_expires_in
-
ActiveSupport::Deprecation.warn('Setting :expires_in on read has been deprecated in favor of setting it on write.', caller)
-
if entry.created_at + deprecated_expires_in.to_f <= Time.now.to_f
-
delete_entry(key, options)
-
entry = nil
-
end
-
end
-
end
-
entry
-
end
-
rescue
-
nil
-
end
-
-
1
def write_entry(key, entry, options)
-
file_name = key_file_path(key)
-
ensure_cache_path(File.dirname(file_name))
-
File.atomic_write(file_name, cache_path) {|f| Marshal.dump(entry, f)}
-
true
-
end
-
-
1
def delete_entry(key, options)
-
file_name = key_file_path(key)
-
if File.exist?(file_name)
-
begin
-
File.delete(file_name)
-
delete_empty_directories(File.dirname(file_name))
-
true
-
rescue => e
-
# Just in case the error was caused by another process deleting the file first.
-
raise e if File.exist?(file_name)
-
false
-
end
-
end
-
end
-
-
1
private
-
# Lock a file for a block so only one process can modify it at a time.
-
1
def lock_file(file_name, &block) # :nodoc:
-
if File.exist?(file_name)
-
File.open(file_name, 'r+') do |f|
-
begin
-
f.flock File::LOCK_EX
-
yield
-
ensure
-
f.flock File::LOCK_UN
-
end
-
end
-
else
-
yield
-
end
-
end
-
-
# Translate a key into a file path.
-
1
def key_file_path(key)
-
fname = Rack::Utils.escape(key)
-
hash = Zlib.adler32(fname)
-
hash, dir_1 = hash.divmod(0x1000)
-
dir_2 = hash.modulo(0x1000)
-
fname_paths = []
-
# Make sure file name is < 255 characters so it doesn't exceed file system limits.
-
if fname.size <= 255
-
fname_paths << fname
-
else
-
while fname.size <= 255
-
fname_path << fname[0, 255]
-
fname = fname[255, -1]
-
end
-
end
-
File.join(cache_path, DIR_FORMATTER % dir_1, DIR_FORMATTER % dir_2, *fname_paths)
-
end
-
-
# Translate a file path into a key.
-
1
def file_path_key(path)
-
fname = path[cache_path.size, path.size].split(File::SEPARATOR, 4).last
-
Rack::Utils.unescape(fname)
-
end
-
-
# Delete empty directories in the cache.
-
1
def delete_empty_directories(dir)
-
return if dir == cache_path
-
if Dir.entries(dir).reject{|f| f.in?(['.', '..'])}.empty?
-
File.delete(dir) rescue nil
-
delete_empty_directories(File.dirname(dir))
-
end
-
end
-
-
# Make sure a file path's directories exist.
-
1
def ensure_cache_path(path)
-
FileUtils.makedirs(path) unless File.exist?(path)
-
end
-
-
1
def search_dir(dir, &callback)
-
Dir.foreach(dir) do |d|
-
next if d == "." || d == ".."
-
name = File.join(dir, d)
-
if File.directory?(name)
-
search_dir(name, &callback)
-
else
-
callback.call name
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/duplicable'
-
1
require 'active_support/core_ext/string/inflections'
-
-
1
module ActiveSupport
-
1
module Cache
-
1
module Strategy
-
# Caches that implement LocalCache will be backed by an in memory cache for the
-
# duration of a block. Repeated calls to the cache for the same key will hit the
-
# in memory cache for faster access.
-
1
module LocalCache
-
# Simple memory backed cache. This cache is not thread safe and is intended only
-
# for serving as a temporary memory cache for a single thread.
-
1
class LocalStore < Store
-
1
def initialize
-
super
-
@data = {}
-
end
-
-
# Don't allow synchronizing since it isn't thread safe,
-
1
def synchronize # :nodoc:
-
yield
-
end
-
-
1
def clear(options = nil)
-
@data.clear
-
end
-
-
1
def read_entry(key, options)
-
@data[key]
-
end
-
-
1
def write_entry(key, value, options)
-
@data[key] = value
-
true
-
end
-
-
1
def delete_entry(key, options)
-
!!@data.delete(key)
-
end
-
end
-
-
# Use a local cache for the duration of block.
-
1
def with_local_cache
-
save_val = Thread.current[thread_local_key]
-
begin
-
Thread.current[thread_local_key] = LocalStore.new
-
yield
-
ensure
-
Thread.current[thread_local_key] = save_val
-
end
-
end
-
-
#--
-
# This class wraps up local storage for middlewares. Only the middleware method should
-
# construct them.
-
1
class Middleware # :nodoc:
-
1
attr_reader :name, :thread_local_key
-
-
1
def initialize(name, thread_local_key)
-
1
@name = name
-
1
@thread_local_key = thread_local_key
-
1
@app = nil
-
end
-
-
1
def new(app)
-
1
@app = app
-
1
self
-
end
-
-
1
def call(env)
-
Thread.current[thread_local_key] = LocalStore.new
-
@app.call(env)
-
ensure
-
Thread.current[thread_local_key] = nil
-
end
-
end
-
-
# Middleware class can be inserted as a Rack handler to be local cache for the
-
# duration of request.
-
1
def middleware
-
@middleware ||= Middleware.new(
-
"ActiveSupport::Cache::Strategy::LocalCache",
-
1
thread_local_key)
-
end
-
-
1
def clear(options = nil) # :nodoc:
-
local_cache.clear(options) if local_cache
-
super
-
end
-
-
1
def cleanup(options = nil) # :nodoc:
-
local_cache.clear(options) if local_cache
-
super
-
end
-
-
1
def increment(name, amount = 1, options = nil) # :nodoc:
-
value = bypass_local_cache{super}
-
if local_cache
-
local_cache.mute do
-
if value
-
local_cache.write(name, value, options)
-
else
-
local_cache.delete(name, options)
-
end
-
end
-
end
-
value
-
end
-
-
1
def decrement(name, amount = 1, options = nil) # :nodoc:
-
value = bypass_local_cache{super}
-
if local_cache
-
local_cache.mute do
-
if value
-
local_cache.write(name, value, options)
-
else
-
local_cache.delete(name, options)
-
end
-
end
-
end
-
value
-
end
-
-
1
protected
-
1
def read_entry(key, options) # :nodoc:
-
if local_cache
-
entry = local_cache.read_entry(key, options)
-
unless entry
-
entry = super
-
local_cache.write_entry(key, entry, options)
-
end
-
entry
-
else
-
super
-
end
-
end
-
-
1
def write_entry(key, entry, options) # :nodoc:
-
local_cache.write_entry(key, entry, options) if local_cache
-
super
-
end
-
-
1
def delete_entry(key, options) # :nodoc:
-
local_cache.delete_entry(key, options) if local_cache
-
super
-
end
-
-
1
private
-
1
def thread_local_key
-
1
@thread_local_key ||= "#{self.class.name.underscore}_local_cache_#{object_id}".gsub(/[\/-]/, '_').to_sym
-
end
-
-
1
def local_cache
-
Thread.current[thread_local_key]
-
end
-
-
1
def bypass_local_cache
-
save_cache = Thread.current[thread_local_key]
-
begin
-
Thread.current[thread_local_key] = nil
-
yield
-
ensure
-
Thread.current[thread_local_key] = save_cache
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/concern'
-
1
require 'active_support/descendants_tracker'
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/kernel/reporting'
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
1
require 'active_support/core_ext/object/inclusion'
-
-
1
module ActiveSupport
-
# \Callbacks are code hooks that are run at key points in an object's lifecycle.
-
# The typical use case is to have a base class define a set of callbacks relevant
-
# to the other functionality it supplies, so that subclasses can install callbacks
-
# that enhance or modify the base functionality without needing to override
-
# or redefine methods of the base class.
-
#
-
# Mixing in this module allows you to define the events in the object's lifecycle
-
# that will support callbacks (via +ClassMethods.define_callbacks+), set the instance
-
# methods, procs, or callback objects to be called (via +ClassMethods.set_callback+),
-
# and run the installed callbacks at the appropriate times (via +run_callbacks+).
-
#
-
# Three kinds of callbacks are supported: before callbacks, run before a certain event;
-
# after callbacks, run after the event; and around callbacks, blocks that surround the
-
# event, triggering it when they yield. Callback code can be contained in instance
-
# methods, procs or lambdas, or callback objects that respond to certain predetermined
-
# methods. See +ClassMethods.set_callback+ for details.
-
#
-
# ==== Example
-
#
-
# class Record
-
# include ActiveSupport::Callbacks
-
# define_callbacks :save
-
#
-
# def save
-
# run_callbacks :save do
-
# puts "- save"
-
# end
-
# end
-
# end
-
#
-
# class PersonRecord < Record
-
# set_callback :save, :before, :saving_message
-
# def saving_message
-
# puts "saving..."
-
# end
-
#
-
# set_callback :save, :after do |object|
-
# puts "saved"
-
# end
-
# end
-
#
-
# person = PersonRecord.new
-
# person.save
-
#
-
# Output:
-
# saving...
-
# - save
-
# saved
-
#
-
1
module Callbacks
-
1
extend Concern
-
-
1
included do
-
6
extend ActiveSupport::DescendantsTracker
-
end
-
-
# Runs the callbacks for the given event.
-
#
-
# Calls the before and around callbacks in the order they were set, yields
-
# the block (if given one), and then runs the after callbacks in reverse order.
-
# Optionally accepts a key, which will be used to compile an optimized callback
-
# method for each key. See +ClassMethods.define_callbacks+ for more information.
-
#
-
# If the callback chain was halted, returns +false+. Otherwise returns the result
-
# of the block, or +true+ if no block is given.
-
#
-
# run_callbacks :save do
-
# save
-
# end
-
#
-
1
def run_callbacks(kind, *args, &block)
-
5
send("_run_#{kind}_callbacks", *args, &block)
-
end
-
-
1
class Callback #:nodoc:#
-
1
@@_callback_sequence = 0
-
-
1
attr_accessor :chain, :filter, :kind, :options, :per_key, :klass, :raw_filter
-
-
1
def initialize(chain, filter, kind, options, klass)
-
38
@chain, @kind, @klass = chain, kind, klass
-
38
normalize_options!(options)
-
-
38
@per_key = options.delete(:per_key)
-
38
@raw_filter, @options = filter, options
-
38
@filter = _compile_filter(filter)
-
38
@compiled_options = _compile_options(options)
-
38
@callback_id = next_id
-
-
38
_compile_per_key_options
-
end
-
-
1
def clone(chain, klass)
-
obj = super()
-
obj.chain = chain
-
obj.klass = klass
-
obj.per_key = @per_key.dup
-
obj.options = @options.dup
-
obj.per_key[:if] = @per_key[:if].dup
-
obj.per_key[:unless] = @per_key[:unless].dup
-
obj.options[:if] = @options[:if].dup
-
obj.options[:unless] = @options[:unless].dup
-
obj
-
end
-
-
1
def normalize_options!(options)
-
38
options[:if] = Array.wrap(options[:if])
-
38
options[:unless] = Array.wrap(options[:unless])
-
-
38
options[:per_key] ||= {}
-
38
options[:per_key][:if] = Array.wrap(options[:per_key][:if])
-
38
options[:per_key][:unless] = Array.wrap(options[:per_key][:unless])
-
end
-
-
1
def name
-
9
chain.name
-
end
-
-
1
def next_id
-
88
@@_callback_sequence += 1
-
end
-
-
1
def matches?(_kind, _filter)
-
49
@kind == _kind && @filter == _filter
-
end
-
-
1
def _update_filter(filter_options, new_options)
-
filter_options[:if].push(new_options[:unless]) if new_options.key?(:unless)
-
filter_options[:unless].push(new_options[:if]) if new_options.key?(:if)
-
end
-
-
1
def recompile!(_options, _per_key)
-
_update_filter(self.options, _options)
-
_update_filter(self.per_key, _per_key)
-
-
@callback_id = next_id
-
@filter = _compile_filter(@raw_filter)
-
@compiled_options = _compile_options(@options)
-
_compile_per_key_options
-
end
-
-
1
def _compile_per_key_options
-
38
key_options = _compile_options(@per_key)
-
-
38
@klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
def _one_time_conditions_valid_#{@callback_id}?
-
true #{key_options[0]}
-
end
-
RUBY_EVAL
-
end
-
-
# This will supply contents for before and around filters, and no
-
# contents for after filters (for the forward pass).
-
1
def start(key=nil, object=nil)
-
96
return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
-
-
# options[0] is the compiled form of supplied conditions
-
# options[1] is the "end" for the conditional
-
#
-
96
case @kind
-
when :before
-
# if condition # before_save :filter_name, :if => :condition
-
# filter_name
-
# end
-
80
filter = <<-RUBY_EVAL
-
unless halted
-
# This double assignment is to prevent warnings in 1.9.3. I would
-
# remove the `result` variable, but apparently some other
-
# generated code is depending on this variable being set sometimes
-
# and sometimes not.
-
result = result = #{@filter}
-
halted = (#{chain.config[:terminator]})
-
end
-
RUBY_EVAL
-
-
80
[@compiled_options[0], filter, @compiled_options[1]].compact.join("\n")
-
when :around
-
# Compile around filters with conditions into proxy methods
-
# that contain the conditions.
-
#
-
# For `around_save :filter_name, :if => :condition':
-
#
-
# def _conditional_callback_save_17
-
# if condition
-
# filter_name do
-
# yield self
-
# end
-
# else
-
# yield self
-
# end
-
# end
-
#
-
name = "_conditional_callback_#{@kind}_#{next_id}"
-
@klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
def #{name}(halted)
-
#{@compiled_options[0] || "if true"} && !halted
-
#{@filter} do
-
yield self
-
end
-
else
-
yield self
-
end
-
end
-
RUBY_EVAL
-
"#{name}(halted) do"
-
end
-
end
-
-
# This will supply contents for around and after filters, but not
-
# before filters (for the backward pass).
-
1
def end(key=nil, object=nil)
-
96
return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
-
-
96
case @kind
-
when :after
-
# if condition # after_save :filter_name, :if => :condition
-
# filter_name
-
# end
-
16
[@compiled_options[0], @filter, @compiled_options[1]].compact.join("\n")
-
when :around
-
<<-RUBY_EVAL
-
value
-
end
-
RUBY_EVAL
-
end
-
end
-
-
1
private
-
-
# Options support the same options as filters themselves (and support
-
# symbols, string, procs, and objects), so compile a conditional
-
# expression based on the options
-
1
def _compile_options(options)
-
76
return [] if options[:if].empty? && options[:unless].empty?
-
-
6
conditions = []
-
-
6
unless options[:if].empty?
-
6
conditions << Array.wrap(_compile_filter(options[:if]))
-
end
-
-
6
unless options[:unless].empty?
-
conditions << Array.wrap(_compile_filter(options[:unless])).map {|f| "!#{f}"}
-
end
-
-
6
["if #{conditions.flatten.join(" && ")}", "end"]
-
end
-
-
# Filters support:
-
#
-
# Arrays:: Used in conditions. This is used to specify
-
# multiple conditions. Used internally to
-
# merge conditions from skip_* filters
-
# Symbols:: A method to call
-
# Strings:: Some content to evaluate
-
# Procs:: A proc to call with the object
-
# Objects:: An object with a before_foo method on it to call
-
#
-
# All of these objects are compiled into methods and handled
-
# the same after this point:
-
#
-
# Arrays:: Merged together into a single filter
-
# Symbols:: Already methods
-
# Strings:: class_eval'ed into methods
-
# Procs:: define_method'ed into methods
-
# Objects::
-
# a method is created that calls the before_foo method
-
# on the object.
-
#
-
1
def _compile_filter(filter)
-
50
method_name = "_callback_#{@kind}_#{next_id}"
-
50
case filter
-
when Array
-
12
filter.map {|f| _compile_filter(f)}
-
when Symbol
-
19
filter
-
when String
-
8
"(#{filter})"
-
when Proc
-
8
@klass.send(:define_method, method_name, &filter)
-
8
return method_name if filter.arity <= 0
-
-
method_name << (filter.arity == 1 ? "(self)" : " self, Proc.new ")
-
else
-
9
@klass.send(:define_method, "#{method_name}_object") { filter }
-
-
9
_normalize_legacy_filter(kind, filter)
-
9
scopes = Array.wrap(chain.config[:scope])
-
18
method_to_call = scopes.map{ |s| s.is_a?(Symbol) ? send(s) : s }.join("_")
-
-
9
@klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
def #{method_name}(&blk)
-
#{method_name}_object.send(:#{method_to_call}, self, &blk)
-
end
-
RUBY_EVAL
-
-
9
method_name
-
end
-
end
-
-
1
def _normalize_legacy_filter(kind, filter)
-
9
if !filter.respond_to?(kind) && filter.respond_to?(:filter)
-
filter.singleton_class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
def #{kind}(context, &block) filter(context, &block) end
-
RUBY_EVAL
-
9
elsif filter.respond_to?(:before) && filter.respond_to?(:after) && kind == :around
-
def filter.around(context)
-
should_continue = before(context)
-
yield if should_continue
-
after(context)
-
end
-
end
-
end
-
end
-
-
# An Array with a compile method
-
1
class CallbackChain < Array #:nodoc:#
-
1
attr_reader :name, :config
-
-
1
def initialize(name, config)
-
19
@name = name
-
19
@config = {
-
:terminator => "false",
-
:rescuable => false,
-
:scope => [ :kind ]
-
}.merge(config)
-
end
-
-
1
def compile(key=nil, object=nil)
-
66
method = []
-
66
method << "value = nil"
-
66
method << "halted = false"
-
-
66
each do |callback|
-
96
method << callback.start(key, object)
-
end
-
-
66
if config[:rescuable]
-
1
method << "rescued_error = nil"
-
1
method << "begin"
-
end
-
-
66
method << "value = yield if block_given? && !halted"
-
-
66
if config[:rescuable]
-
1
method << "rescue Exception => e"
-
1
method << "rescued_error = e"
-
1
method << "end"
-
end
-
-
66
reverse_each do |callback|
-
96
method << callback.end(key, object)
-
end
-
-
66
method << "raise rescued_error if rescued_error" if config[:rescuable]
-
66
method << "halted ? false : (block_given? ? value : true)"
-
66
method.compact.join("\n")
-
end
-
end
-
-
1
module ClassMethods
-
# Generate the internal runner method called by +run_callbacks+.
-
1
def __define_runner(symbol) #:nodoc:
-
66
body = send("_#{symbol}_callbacks").compile
-
-
66
silence_warnings do
-
66
undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks")
-
66
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
def _run_#{symbol}_callbacks(key = nil, &blk)
-
if key
-
name = "_run__\#{self.class.name.hash.abs}__#{symbol}__\#{key.hash.abs}__callbacks"
-
-
unless respond_to?(name)
-
self.class.__create_keyed_callback(name, :#{symbol}, self, &blk)
-
end
-
-
send(name, &blk)
-
else
-
#{body}
-
end
-
end
-
private :_run_#{symbol}_callbacks
-
RUBY_EVAL
-
end
-
end
-
-
# This is called the first time a callback is called with a particular
-
# key. It creates a new callback method for the key, calculating
-
# which callbacks can be omitted because of per_key conditions.
-
#
-
1
def __create_keyed_callback(name, kind, object, &blk) #:nodoc:
-
@_keyed_callbacks ||= {}
-
@_keyed_callbacks[name] ||= begin
-
str = send("_#{kind}_callbacks").compile(name, object)
-
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
def #{name}() #{str} end
-
protected :#{name}
-
RUBY_EVAL
-
true
-
end
-
end
-
-
# This is used internally to append, prepend and skip callbacks to the
-
# CallbackChain.
-
#
-
1
def __update_callbacks(name, filters = [], block = nil) #:nodoc:
-
38
type = filters.first.in?([:before, :after, :around]) ? filters.shift : :before
-
38
options = filters.last.is_a?(Hash) ? filters.pop : {}
-
38
filters.unshift(block) if block
-
-
38
([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse.each do |target|
-
47
chain = target.send("_#{name}_callbacks")
-
47
yield target, chain.dup, type, filters, options
-
47
target.__define_runner(name)
-
end
-
end
-
-
# Install a callback for the given event.
-
#
-
# set_callback :save, :before, :before_meth
-
# set_callback :save, :after, :after_meth, :if => :condition
-
# set_callback :save, :around, lambda { |r| stuff; result = yield; stuff }
-
#
-
# The second arguments indicates whether the callback is to be run +:before+,
-
# +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This
-
# means the first example above can also be written as:
-
#
-
# set_callback :save, :before_meth
-
#
-
# The callback can specified as a symbol naming an instance method; as a proc,
-
# lambda, or block; as a string to be instance evaluated; or as an object that
-
# responds to a certain method determined by the <tt>:scope</tt> argument to
-
# +define_callback+.
-
#
-
# If a proc, lambda, or block is given, its body is evaluated in the context
-
# of the current object. It can also optionally accept the current object as
-
# an argument.
-
#
-
# Before and around callbacks are called in the order that they are set; after
-
# callbacks are called in the reverse order.
-
#
-
# Around callbacks can access the return value from the event, if it
-
# wasn't halted, from the +yield+ call.
-
#
-
# ===== Options
-
#
-
# * <tt>:if</tt> - A symbol naming an instance method or a proc; the callback
-
# will be called only when it returns a true value.
-
# * <tt>:unless</tt> - A symbol naming an instance method or a proc; the callback
-
# will be called only when it returns a false value.
-
# * <tt>:prepend</tt> - If true, the callback will be prepended to the existing
-
# chain rather than appended.
-
# * <tt>:per_key</tt> - A hash with <tt>:if</tt> and <tt>:unless</tt> options;
-
# see "Per-key conditions" below.
-
#
-
# ===== Per-key conditions
-
#
-
# When creating or skipping callbacks, you can specify conditions that
-
# are always the same for a given key. For instance, in Action Pack,
-
# we convert :only and :except conditions into per-key conditions.
-
#
-
# before_filter :authenticate, :except => "index"
-
#
-
# becomes
-
#
-
# set_callback :process_action, :before, :authenticate, :per_key => {:unless => proc {|c| c.action_name == "index"}}
-
#
-
# Per-key conditions are evaluated only once per use of a given key.
-
# In the case of the above example, you would do:
-
#
-
# run_callbacks(:process_action, action_name) { ... dispatch stuff ... }
-
#
-
# In that case, each action_name would get its own compiled callback
-
# method that took into consideration the per_key conditions. This
-
# is a speed improvement for ActionPack.
-
#
-
1
def set_callback(name, *filter_list, &block)
-
38
mapped = nil
-
-
38
__update_callbacks(name, filter_list, block) do |target, chain, type, filters, options|
-
mapped ||= filters.map do |filter|
-
38
Callback.new(chain, filter, type, options.dup, self)
-
47
end
-
-
47
filters.each do |filter|
-
96
chain.delete_if {|c| c.matches?(type, filter) }
-
end
-
-
47
options[:prepend] ? chain.unshift(*(mapped.reverse)) : chain.push(*mapped)
-
-
47
target.send("_#{name}_callbacks=", chain)
-
end
-
end
-
-
# Skip a previously set callback. Like +set_callback+, <tt>:if</tt> or <tt>:unless</tt>
-
# options may be passed in order to control when the callback is skipped.
-
#
-
# class Writer < Person
-
# skip_callback :validate, :before, :check_membership, :if => lambda { self.age > 18 }
-
# end
-
#
-
1
def skip_callback(name, *filter_list, &block)
-
__update_callbacks(name, filter_list, block) do |target, chain, type, filters, options|
-
filters.each do |filter|
-
filter = chain.find {|c| c.matches?(type, filter) }
-
-
if filter && options.any?
-
new_filter = filter.clone(chain, self)
-
chain.insert(chain.index(filter), new_filter)
-
new_filter.recompile!(options, options[:per_key] || {})
-
end
-
-
chain.delete(filter)
-
end
-
target.send("_#{name}_callbacks=", chain)
-
end
-
end
-
-
# Remove all set callbacks for the given event.
-
#
-
1
def reset_callbacks(symbol)
-
callbacks = send("_#{symbol}_callbacks")
-
-
ActiveSupport::DescendantsTracker.descendants(self).each do |target|
-
chain = target.send("_#{symbol}_callbacks").dup
-
callbacks.each { |c| chain.delete(c) }
-
target.send("_#{symbol}_callbacks=", chain)
-
target.__define_runner(symbol)
-
end
-
-
self.send("_#{symbol}_callbacks=", callbacks.dup.clear)
-
-
__define_runner(symbol)
-
end
-
-
# Define sets of events in the object lifecycle that support callbacks.
-
#
-
# define_callbacks :validate
-
# define_callbacks :initialize, :save, :destroy
-
#
-
# ===== Options
-
#
-
# * <tt>:terminator</tt> - Determines when a before filter will halt the callback
-
# chain, preventing following callbacks from being called and the event from being
-
# triggered. This is a string to be eval'ed. The result of the callback is available
-
# in the <tt>result</tt> variable.
-
#
-
# define_callbacks :validate, :terminator => "result == false"
-
#
-
# In this example, if any before validate callbacks returns +false+,
-
# other callbacks are not executed. Defaults to "false", meaning no value
-
# halts the chain.
-
#
-
# * <tt>:rescuable</tt> - By default, after filters are not executed if
-
# the given block or a before filter raises an error. By setting this option
-
# to <tt>true</tt> exception raised by given block is stored and after
-
# executing all the after callbacks the stored exception is raised.
-
#
-
# * <tt>:scope</tt> - Indicates which methods should be executed when an object
-
# is used as a callback.
-
#
-
# class Audit
-
# def before(caller)
-
# puts 'Audit: before is called'
-
# end
-
#
-
# def before_save(caller)
-
# puts 'Audit: before_save is called'
-
# end
-
# end
-
#
-
# class Account
-
# include ActiveSupport::Callbacks
-
#
-
# define_callbacks :save
-
# set_callback :save, :before, Audit.new
-
#
-
# def save
-
# run_callbacks :save do
-
# puts 'save in main'
-
# end
-
# end
-
# end
-
#
-
# In the above case whenever you save an account the method <tt>Audit#before</tt> will
-
# be called. On the other hand
-
#
-
# define_callbacks :save, :scope => [:kind, :name]
-
#
-
# would trigger <tt>Audit#before_save</tt> instead. That's constructed by calling
-
# <tt>#{kind}_#{name}</tt> on the given instance. In this case "kind" is "before" and
-
# "name" is "save". In this context +:kind+ and +:name+ have special meanings: +:kind+
-
# refers to the kind of callback (before/after/around) and +:name+ refers to the
-
# method on which callbacks are being defined.
-
#
-
# A declaration like
-
#
-
# define_callbacks :save, :scope => [:name]
-
#
-
# would call <tt>Audit#save</tt>.
-
#
-
1
def define_callbacks(*callbacks)
-
16
config = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
-
16
callbacks.each do |callback|
-
19
class_attribute "_#{callback}_callbacks"
-
19
send("_#{callback}_callbacks=", CallbackChain.new(callback, config))
-
19
__define_runner(callback)
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/concern'
-
1
require 'active_support/ordered_options'
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
1
require 'active_support/core_ext/module/delegation'
-
1
require 'active_support/core_ext/array/extract_options'
-
-
1
module ActiveSupport
-
# Configurable provides a <tt>config</tt> method to store and retrieve
-
# configuration options as an <tt>OrderedHash</tt>.
-
1
module Configurable
-
1
extend ActiveSupport::Concern
-
-
1
class Configuration < ActiveSupport::InheritableOptions
-
1
def compile_methods!
-
1
self.class.compile_methods!(keys)
-
end
-
-
# compiles reader methods so we don't have to go through method_missing
-
1
def self.compile_methods!(keys)
-
17
keys.reject { |m| method_defined?(m) }.each do |key|
-
16
class_eval <<-RUBY, __FILE__, __LINE__ + 1
-
def #{key}; _get(#{key.inspect}); end
-
RUBY
-
end
-
end
-
end
-
-
1
module ClassMethods
-
1
def config
-
@_config ||= if respond_to?(:superclass) && superclass.respond_to?(:config)
-
6
superclass.config.inheritable_copy
-
else
-
# create a new "anonymous" class that will host the compiled reader methods
-
1
Class.new(Configuration).new
-
43
end
-
end
-
-
1
def configure
-
yield config
-
end
-
-
# Allows you to add shortcut so that you don't have to refer to attribute through config.
-
# Also look at the example for config to contrast.
-
#
-
# class User
-
# include ActiveSupport::Configurable
-
# config_accessor :allowed_access
-
# end
-
#
-
# user = User.new
-
# user.allowed_access = true
-
# user.allowed_access # => true
-
#
-
1
def config_accessor(*names)
-
9
options = names.extract_options!
-
-
9
names.each do |name|
-
14
reader, line = "def #{name}; config.#{name}; end", __LINE__
-
14
writer, line = "def #{name}=(value); config.#{name} = value; end", __LINE__
-
-
14
singleton_class.class_eval reader, __FILE__, line
-
14
singleton_class.class_eval writer, __FILE__, line
-
14
class_eval reader, __FILE__, line unless options[:instance_reader] == false
-
14
class_eval writer, __FILE__, line unless options[:instance_writer] == false
-
end
-
end
-
end
-
-
# Reads and writes attributes from a configuration <tt>OrderedHash</tt>.
-
#
-
# require 'active_support/configurable'
-
#
-
# class User
-
# include ActiveSupport::Configurable
-
# end
-
#
-
# user = User.new
-
#
-
# user.config.allowed_access = true
-
# user.config.level = 1
-
#
-
# user.config.allowed_access # => true
-
# user.config.level # => 1
-
#
-
1
def config
-
@_config ||= self.class.config.inheritable_copy
-
end
-
end
-
end
-
-
1
Dir["#{File.dirname(__FILE__)}/core_ext/*.rb"].sort.each do |path|
-
24
require "active_support/core_ext/#{File.basename(path, '.rb')}"
-
end
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_support/core_ext/array/access'
-
1
require 'active_support/core_ext/array/uniq_by'
-
1
require 'active_support/core_ext/array/conversions'
-
1
require 'active_support/core_ext/array/extract_options'
-
1
require 'active_support/core_ext/array/grouping'
-
1
require 'active_support/core_ext/array/random_access'
-
1
class Array
-
# Returns the tail of the array from +position+.
-
#
-
# %w( a b c d ).from(0) # => %w( a b c d )
-
# %w( a b c d ).from(2) # => %w( c d )
-
# %w( a b c d ).from(10) # => %w()
-
# %w().from(0) # => %w()
-
1
def from(position)
-
self[position, length] || []
-
end
-
-
# Returns the beginning of the array up to +position+.
-
#
-
# %w( a b c d ).to(0) # => %w( a )
-
# %w( a b c d ).to(2) # => %w( a b c )
-
# %w( a b c d ).to(10) # => %w( a b c d )
-
# %w().to(0) # => %w()
-
1
def to(position)
-
self[0..position]
-
end
-
-
# Equal to <tt>self[1]</tt>.
-
1
def second
-
self[1]
-
end
-
-
# Equal to <tt>self[2]</tt>.
-
1
def third
-
self[2]
-
end
-
-
# Equal to <tt>self[3]</tt>.
-
1
def fourth
-
self[3]
-
end
-
-
# Equal to <tt>self[4]</tt>.
-
1
def fifth
-
self[4]
-
end
-
-
# Equal to <tt>self[41]</tt>. Also known as accessing "the reddit".
-
1
def forty_two
-
self[41]
-
end
-
end
-
1
require 'enumerator'
-
-
1
class Array
-
# Splits or iterates over the array in groups of size +number+,
-
# padding any remaining slots with +fill_with+ unless it is +false+.
-
#
-
# %w(1 2 3 4 5 6 7).in_groups_of(3) {|group| p group}
-
# ["1", "2", "3"]
-
# ["4", "5", "6"]
-
# ["7", nil, nil]
-
#
-
# %w(1 2 3).in_groups_of(2, ' ') {|group| p group}
-
# ["1", "2"]
-
# ["3", " "]
-
#
-
# %w(1 2 3).in_groups_of(2, false) {|group| p group}
-
# ["1", "2"]
-
# ["3"]
-
1
def in_groups_of(number, fill_with = nil)
-
if fill_with == false
-
collection = self
-
else
-
# size % number gives how many extra we have;
-
# subtracting from number gives how many to add;
-
# modulo number ensures we don't add group of just fill.
-
padding = (number - size % number) % number
-
collection = dup.concat([fill_with] * padding)
-
end
-
-
if block_given?
-
collection.each_slice(number) { |slice| yield(slice) }
-
else
-
groups = []
-
collection.each_slice(number) { |group| groups << group }
-
groups
-
end
-
end
-
-
# Splits or iterates over the array in +number+ of groups, padding any
-
# remaining slots with +fill_with+ unless it is +false+.
-
#
-
# %w(1 2 3 4 5 6 7 8 9 10).in_groups(3) {|group| p group}
-
# ["1", "2", "3", "4"]
-
# ["5", "6", "7", nil]
-
# ["8", "9", "10", nil]
-
#
-
# %w(1 2 3 4 5 6 7).in_groups(3, ' ') {|group| p group}
-
# ["1", "2", "3"]
-
# ["4", "5", " "]
-
# ["6", "7", " "]
-
#
-
# %w(1 2 3 4 5 6 7).in_groups(3, false) {|group| p group}
-
# ["1", "2", "3"]
-
# ["4", "5"]
-
# ["6", "7"]
-
1
def in_groups(number, fill_with = nil)
-
# size / number gives minor group size;
-
# size % number gives how many objects need extra accommodation;
-
# each group hold either division or division + 1 items.
-
division = size / number
-
modulo = size % number
-
-
# create a new array avoiding dup
-
groups = []
-
start = 0
-
-
number.times do |index|
-
length = division + (modulo > 0 && modulo > index ? 1 : 0)
-
padding = fill_with != false &&
-
modulo > 0 && length == division ? 1 : 0
-
groups << slice(start, length).concat([fill_with] * padding)
-
start += length
-
end
-
-
if block_given?
-
groups.each { |g| yield(g) }
-
else
-
groups
-
end
-
end
-
-
# Divides the array into one or more subarrays based on a delimiting +value+
-
# or the result of an optional block.
-
#
-
# [1, 2, 3, 4, 5].split(3) # => [[1, 2], [4, 5]]
-
# (1..10).to_a.split { |i| i % 3 == 0 } # => [[1, 2], [4, 5], [7, 8], [10]]
-
1
def split(value = nil)
-
using_block = block_given?
-
-
inject([[]]) do |results, element|
-
if (using_block && yield(element)) || (value == element)
-
results << []
-
else
-
results.last << element
-
end
-
-
results
-
end
-
end
-
end
-
1
class Array
-
# Backport of Array#sample based on Marc-Andre Lafortune's https://github.com/marcandre/backports/
-
# Returns a random element or +n+ random elements from the array.
-
# If the array is empty and +n+ is nil, returns <tt>nil</tt>.
-
# If +n+ is passed and its value is less than 0, it raises an +ArgumentError+ exception.
-
# If the value of +n+ is equal or greater than 0 it returns <tt>[]</tt>.
-
#
-
# [1,2,3,4,5,6].sample # => 4
-
# [1,2,3,4,5,6].sample(3) # => [2, 4, 5]
-
# [1,2,3,4,5,6].sample(-3) # => ArgumentError: negative array size
-
# [].sample # => nil
-
# [].sample(3) # => []
-
def sample(n=nil)
-
return self[Kernel.rand(size)] if n.nil?
-
n = n.to_int
-
rescue Exception => e
-
raise TypeError, "Coercion error: #{n.inspect}.to_int => Integer failed:\n(#{e.message})"
-
else
-
raise TypeError, "Coercion error: obj.to_int did NOT return an Integer (was #{n.class})" unless n.kind_of? Integer
-
raise ArgumentError, "negative array size" if n < 0
-
n = size if n > size
-
result = Array.new(self)
-
n.times do |i|
-
r = i + Kernel.rand(size - i)
-
result[i], result[r] = result[r], result[i]
-
end
-
result[n..size] = []
-
result
-
1
end unless method_defined? :sample
-
end
-
1
class Array
-
# Returns an unique array based on the criteria given as a +Proc+.
-
#
-
# [1, 2, 3, 4].uniq_by { |i| i.odd? } # => [1, 2]
-
#
-
1
def uniq_by
-
hash, array = {}, []
-
each { |i| hash[yield(i)] ||= (array << i) }
-
array
-
end
-
-
# Same as uniq_by, but modifies self.
-
1
def uniq_by!
-
replace(uniq_by{ |i| yield(i) })
-
end
-
end
-
1
require 'benchmark'
-
-
1
class << Benchmark
-
1
def ms
-
1000 * realtime { yield }
-
end
-
end
-
1
require 'active_support/core_ext/big_decimal/conversions'
-
1
require 'bigdecimal'
-
-
1
begin
-
1
require 'psych'
-
rescue LoadError
-
end
-
-
1
require 'yaml'
-
-
1
class BigDecimal
-
1
YAML_TAG = 'tag:yaml.org,2002:float'
-
1
YAML_MAPPING = { 'Infinity' => '.Inf', '-Infinity' => '-.Inf', 'NaN' => '.NaN' }
-
-
# This emits the number without any scientific notation.
-
# This is better than self.to_f.to_s since it doesn't lose precision.
-
#
-
# Note that reconstituting YAML floats to native floats may lose precision.
-
1
def to_yaml(opts = {})
-
return super if defined?(YAML::ENGINE) && !YAML::ENGINE.syck?
-
-
YAML.quick_emit(nil, opts) do |out|
-
string = to_s
-
out.scalar(YAML_TAG, YAML_MAPPING[string] || string, :plain)
-
end
-
end
-
-
1
def encode_with(coder)
-
string = to_s
-
coder.represent_scalar(nil, YAML_MAPPING[string] || string)
-
end
-
-
1
def to_d
-
self
-
end
-
-
1
DEFAULT_STRING_FORMAT = 'F'
-
1
def to_formatted_s(format = DEFAULT_STRING_FORMAT)
-
_original_to_s(format)
-
end
-
1
alias_method :_original_to_s, :to_s
-
1
alias_method :to_s, :to_formatted_s
-
end
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/class/attribute_accessors'
-
1
require 'active_support/core_ext/class/inheritable_attributes'
-
1
require 'active_support/core_ext/class/delegating_attributes'
-
1
require 'active_support/core_ext/class/subclasses'
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
1
require 'active_support/core_ext/module/remove_method'
-
1
require 'active_support/core_ext/array/extract_options'
-
-
1
class Class
-
# Declare a class-level attribute whose value is inheritable by subclasses.
-
# Subclasses can change their own value and it will not impact parent class.
-
#
-
# class Base
-
# class_attribute :setting
-
# end
-
#
-
# class Subclass < Base
-
# end
-
#
-
# Base.setting = true
-
# Subclass.setting # => true
-
# Subclass.setting = false
-
# Subclass.setting # => false
-
# Base.setting # => true
-
#
-
# In the above case as long as Subclass does not assign a value to setting
-
# by performing <tt>Subclass.setting = _something_ </tt>, <tt>Subclass.setting</tt>
-
# would read value assigned to parent class. Once Subclass assigns a value then
-
# the value assigned by Subclass would be returned.
-
#
-
# This matches normal Ruby method inheritance: think of writing an attribute
-
# on a subclass as overriding the reader method. However, you need to be aware
-
# when using +class_attribute+ with mutable structures as +Array+ or +Hash+.
-
# In such cases, you don't want to do changes in places but use setters:
-
#
-
# Base.setting = []
-
# Base.setting # => []
-
# Subclass.setting # => []
-
#
-
# # Appending in child changes both parent and child because it is the same object:
-
# Subclass.setting << :foo
-
# Base.setting # => [:foo]
-
# Subclass.setting # => [:foo]
-
#
-
# # Use setters to not propagate changes:
-
# Base.setting = []
-
# Subclass.setting += [:foo]
-
# Base.setting # => []
-
# Subclass.setting # => [:foo]
-
#
-
# For convenience, a query method is defined as well:
-
#
-
# Subclass.setting? # => false
-
#
-
# Instances may overwrite the class value in the same way:
-
#
-
# Base.setting = true
-
# object = Base.new
-
# object.setting # => true
-
# object.setting = false
-
# object.setting # => false
-
# Base.setting # => true
-
#
-
# To opt out of the instance reader method, pass :instance_reader => false.
-
#
-
# object.setting # => NoMethodError
-
# object.setting? # => NoMethodError
-
#
-
# To opt out of the instance writer method, pass :instance_writer => false.
-
#
-
# object.setting = false # => NoMethodError
-
1
def class_attribute(*attrs)
-
75
options = attrs.extract_options!
-
75
instance_reader = options.fetch(:instance_reader, true)
-
75
instance_writer = options.fetch(:instance_writer, true)
-
-
75
attrs.each do |name|
-
76
class_eval <<-RUBY, __FILE__, __LINE__ + 1
-
def self.#{name}() nil end
-
def self.#{name}?() !!#{name} end
-
-
def self.#{name}=(val)
-
singleton_class.class_eval do
-
remove_possible_method(:#{name})
-
define_method(:#{name}) { val }
-
end
-
-
if singleton_class?
-
class_eval do
-
remove_possible_method(:#{name})
-
def #{name}
-
defined?(@#{name}) ? @#{name} : singleton_class.#{name}
-
end
-
end
-
end
-
val
-
end
-
-
if instance_reader
-
remove_possible_method :#{name}
-
def #{name}
-
defined?(@#{name}) ? @#{name} : self.class.#{name}
-
end
-
-
def #{name}?
-
!!#{name}
-
end
-
end
-
RUBY
-
-
76
attr_writer name if instance_writer
-
end
-
end
-
-
1
private
-
1
def singleton_class?
-
170
!name || '' == name
-
end
-
end
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/array/extract_options'
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
1
require 'active_support/core_ext/module/remove_method'
-
-
1
class Class
-
1
def superclass_delegating_accessor(name, options = {})
-
# Create private _name and _name= methods that can still be used if the public
-
# methods are overridden. This allows
-
_superclass_delegating_accessor("_#{name}")
-
-
# Generate the public methods name, name=, and name?
-
# These methods dispatch to the private _name, and _name= methods, making them
-
# overridable
-
singleton_class.send(:define_method, name) { send("_#{name}") }
-
singleton_class.send(:define_method, "#{name}?") { !!send("_#{name}") }
-
singleton_class.send(:define_method, "#{name}=") { |value| send("_#{name}=", value) }
-
-
# If an instance_reader is needed, generate methods for name and name= on the
-
# class itself, so instances will be able to see them
-
define_method(name) { send("_#{name}") } if options[:instance_reader] != false
-
define_method("#{name}?") { !!send("#{name}") } if options[:instance_reader] != false
-
end
-
-
1
private
-
-
# Take the object being set and store it in a method. This gives us automatic
-
# inheritance behavior, without having to store the object in an instance
-
# variable and look up the superclass chain manually.
-
1
def _stash_object_in_method(object, method, instance_reader = true)
-
singleton_class.remove_possible_method(method)
-
singleton_class.send(:define_method, method) { object }
-
remove_possible_method(method)
-
define_method(method) { object } if instance_reader
-
end
-
-
1
def _superclass_delegating_accessor(name, options = {})
-
singleton_class.send(:define_method, "#{name}=") do |value|
-
_stash_object_in_method(value, name, options[:instance_reader] != false)
-
end
-
send("#{name}=", nil)
-
end
-
-
end
-
1
require 'active_support/core_ext/object/duplicable'
-
1
require 'active_support/core_ext/array/extract_options'
-
1
require 'active_support/deprecation'
-
-
# Retained for backward compatibility. Methods are now included in Class.
-
1
module ClassInheritableAttributes # :nodoc:
-
1
DEPRECATION_WARNING_MESSAGE = "class_inheritable_attribute is deprecated, please use class_attribute method instead. Notice their behavior are slightly different, so refer to class_attribute documentation first"
-
end
-
-
# It is recommended to use <tt>class_attribute</tt> over methods defined in this file. Please
-
# refer to documentation for <tt>class_attribute</tt> for more information. Officially it is not
-
# deprecated but <tt>class_attribute</tt> is faster.
-
#
-
# Allows attributes to be shared within an inheritance hierarchy. Each descendant gets a copy of
-
# their parents' attributes, instead of just a pointer to the same. This means that the child can add elements
-
# to, for example, an array without those additions being shared with either their parent, siblings, or
-
# children. This is unlike the regular class-level attributes that are shared across the entire hierarchy.
-
#
-
# The copies of inheritable parent attributes are added to subclasses when they are created, via the
-
# +inherited+ hook.
-
#
-
# class Person
-
# class_inheritable_accessor :hair_colors
-
# end
-
#
-
# Person.hair_colors = [:brown, :black, :blonde, :red]
-
# Person.hair_colors # => [:brown, :black, :blonde, :red]
-
# Person.new.hair_colors # => [:brown, :black, :blonde, :red]
-
#
-
# To opt out of the instance writer method, pass :instance_writer => false.
-
# To opt out of the instance reader method, pass :instance_reader => false.
-
#
-
# class Person
-
# class_inheritable_accessor :hair_colors :instance_writer => false, :instance_reader => false
-
# end
-
#
-
# Person.new.hair_colors = [:brown] # => NoMethodError
-
# Person.new.hair_colors # => NoMethodError
-
1
class Class # :nodoc:
-
1
def class_inheritable_reader(*syms)
-
ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE
-
options = syms.extract_options!
-
syms.each do |sym|
-
next if sym.is_a?(Hash)
-
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
def self.#{sym} # def self.after_add
-
read_inheritable_attribute(:#{sym}) # read_inheritable_attribute(:after_add)
-
end # end
-
#
-
#{" #
-
def #{sym} # def after_add
-
self.class.#{sym} # self.class.after_add
-
end # end
-
" unless options[:instance_reader] == false } # # the reader above is generated unless options[:instance_reader] == false
-
EOS
-
end
-
end
-
-
1
def class_inheritable_writer(*syms)
-
ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE
-
options = syms.extract_options!
-
syms.each do |sym|
-
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
def self.#{sym}=(obj) # def self.color=(obj)
-
write_inheritable_attribute(:#{sym}, obj) # write_inheritable_attribute(:color, obj)
-
end # end
-
#
-
#{" #
-
def #{sym}=(obj) # def color=(obj)
-
self.class.#{sym} = obj # self.class.color = obj
-
end # end
-
" unless options[:instance_writer] == false } # # the writer above is generated unless options[:instance_writer] == false
-
EOS
-
end
-
end
-
-
1
def class_inheritable_array_writer(*syms)
-
ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE
-
options = syms.extract_options!
-
syms.each do |sym|
-
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
def self.#{sym}=(obj) # def self.levels=(obj)
-
write_inheritable_array(:#{sym}, obj) # write_inheritable_array(:levels, obj)
-
end # end
-
#
-
#{" #
-
def #{sym}=(obj) # def levels=(obj)
-
self.class.#{sym} = obj # self.class.levels = obj
-
end # end
-
" unless options[:instance_writer] == false } # # the writer above is generated unless options[:instance_writer] == false
-
EOS
-
end
-
end
-
-
1
def class_inheritable_hash_writer(*syms)
-
ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE
-
options = syms.extract_options!
-
syms.each do |sym|
-
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
def self.#{sym}=(obj) # def self.nicknames=(obj)
-
write_inheritable_hash(:#{sym}, obj) # write_inheritable_hash(:nicknames, obj)
-
end # end
-
#
-
#{" #
-
def #{sym}=(obj) # def nicknames=(obj)
-
self.class.#{sym} = obj # self.class.nicknames = obj
-
end # end
-
" unless options[:instance_writer] == false } # # the writer above is generated unless options[:instance_writer] == false
-
EOS
-
end
-
end
-
-
1
def class_inheritable_accessor(*syms)
-
class_inheritable_reader(*syms)
-
class_inheritable_writer(*syms)
-
end
-
-
1
def class_inheritable_array(*syms)
-
class_inheritable_reader(*syms)
-
class_inheritable_array_writer(*syms)
-
end
-
-
1
def class_inheritable_hash(*syms)
-
class_inheritable_reader(*syms)
-
class_inheritable_hash_writer(*syms)
-
end
-
-
1
def inheritable_attributes
-
808
@inheritable_attributes ||= EMPTY_INHERITABLE_ATTRIBUTES
-
end
-
-
1
def write_inheritable_attribute(key, value)
-
if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)
-
@inheritable_attributes = {}
-
end
-
inheritable_attributes[key] = value
-
end
-
-
1
def write_inheritable_array(key, elements)
-
write_inheritable_attribute(key, []) if read_inheritable_attribute(key).nil?
-
write_inheritable_attribute(key, read_inheritable_attribute(key) + elements)
-
end
-
-
1
def write_inheritable_hash(key, hash)
-
write_inheritable_attribute(key, {}) if read_inheritable_attribute(key).nil?
-
write_inheritable_attribute(key, read_inheritable_attribute(key).merge(hash))
-
end
-
-
1
def read_inheritable_attribute(key)
-
inheritable_attributes[key]
-
end
-
-
1
def reset_inheritable_attributes
-
ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE
-
@inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES
-
end
-
-
1
private
-
# Prevent this constant from being created multiple times
-
1
EMPTY_INHERITABLE_ATTRIBUTES = {}.freeze
-
-
1
def inherited_with_inheritable_attributes(child)
-
808
inherited_without_inheritable_attributes(child) if respond_to?(:inherited_without_inheritable_attributes)
-
-
808
if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)
-
808
new_inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES
-
else
-
new_inheritable_attributes = Hash[inheritable_attributes.map do |(key, value)|
-
[key, value.duplicable? ? value.dup : value]
-
end]
-
end
-
-
808
child.instance_variable_set('@inheritable_attributes', new_inheritable_attributes)
-
end
-
-
1
alias inherited_without_inheritable_attributes inherited
-
1
alias inherited inherited_with_inheritable_attributes
-
end
-
1
require 'active_support/core_ext/module/anonymous'
-
1
require 'active_support/core_ext/module/reachable'
-
-
1
class Class #:nodoc:
-
1
begin
-
1
ObjectSpace.each_object(Class.new) {}
-
-
1
def descendants
-
descendants = []
-
ObjectSpace.each_object(class << self; self; end) do |k|
-
descendants.unshift k unless k == self
-
end
-
descendants
-
end
-
rescue StandardError # JRuby
-
def descendants
-
descendants = []
-
ObjectSpace.each_object(Class) do |k|
-
descendants.unshift k if k < self
-
end
-
descendants.uniq!
-
descendants
-
end
-
end
-
-
# Returns an array with the direct children of +self+.
-
#
-
# Integer.subclasses # => [Bignum, Fixnum]
-
1
def subclasses
-
subclasses, chain = [], descendants
-
chain.each do |k|
-
subclasses << k unless chain.any? { |c| c > k }
-
end
-
subclasses
-
end
-
end
-
1
require 'active_support/core_ext/object/acts_like'
-
-
1
class Date
-
# Duck-types as a Date-like class. See Object#acts_like?.
-
1
def acts_like_date?
-
true
-
end
-
end
-
1
require 'date'
-
1
require 'active_support/duration'
-
1
require 'active_support/core_ext/object/acts_like'
-
1
require 'active_support/core_ext/date/zones'
-
1
require 'active_support/core_ext/time/zones'
-
-
1
class Date
-
1
DAYS_INTO_WEEK = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6 }
-
-
1
if RUBY_VERSION < '1.9'
-
undef :>>
-
-
# Backported from 1.9. The one in 1.8 leads to incorrect next_month and
-
# friends for dates where the calendar reform is involved. It additionally
-
# prevents an infinite loop fixed in r27013.
-
def >>(n)
-
y, m = (year * 12 + (mon - 1) + n).divmod(12)
-
m, = (m + 1) .divmod(1)
-
d = mday
-
until jd2 = self.class.valid_civil?(y, m, d, start)
-
d -= 1
-
raise ArgumentError, 'invalid date' unless d > 0
-
end
-
self + (jd2 - jd)
-
end
-
end
-
-
1
class << self
-
# Returns a new Date representing the date 1 day ago (i.e. yesterday's date).
-
1
def yesterday
-
::Date.current.yesterday
-
end
-
-
# Returns a new Date representing the date 1 day after today (i.e. tomorrow's date).
-
1
def tomorrow
-
::Date.current.tomorrow
-
end
-
-
# Returns Time.zone.today when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise just returns Date.today.
-
1
def current
-
::Time.zone ? ::Time.zone.today : ::Date.today
-
end
-
end
-
-
# Returns true if the Date object's date lies in the past. Otherwise returns false.
-
1
def past?
-
self < ::Date.current
-
end
-
-
# Returns true if the Date object's date is today.
-
1
def today?
-
self.to_date == ::Date.current # we need the to_date because of DateTime
-
end
-
-
# Returns true if the Date object's date lies in the future.
-
1
def future?
-
self > ::Date.current
-
end
-
-
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
-
# and then subtracts the specified number of seconds.
-
1
def ago(seconds)
-
to_time_in_current_zone.since(-seconds)
-
end
-
-
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
-
# and then adds the specified number of seconds
-
1
def since(seconds)
-
to_time_in_current_zone.since(seconds)
-
end
-
1
alias :in :since
-
-
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
-
1
def beginning_of_day
-
to_time_in_current_zone
-
end
-
1
alias :midnight :beginning_of_day
-
1
alias :at_midnight :beginning_of_day
-
1
alias :at_beginning_of_day :beginning_of_day
-
-
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the end of the day (23:59:59)
-
1
def end_of_day
-
to_time_in_current_zone.end_of_day
-
end
-
-
1
def plus_with_duration(other) #:nodoc:
-
if ActiveSupport::Duration === other
-
other.since(self)
-
else
-
plus_without_duration(other)
-
end
-
end
-
1
alias_method :plus_without_duration, :+
-
1
alias_method :+, :plus_with_duration
-
-
1
def minus_with_duration(other) #:nodoc:
-
if ActiveSupport::Duration === other
-
plus_with_duration(-other)
-
else
-
minus_without_duration(other)
-
end
-
end
-
1
alias_method :minus_without_duration, :-
-
1
alias_method :-, :minus_with_duration
-
-
# Provides precise Date calculations for years, months, and days. The +options+ parameter takes a hash with
-
# any of these keys: <tt>:years</tt>, <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>.
-
1
def advance(options)
-
options = options.dup
-
d = self
-
d = d >> options.delete(:years) * 12 if options[:years]
-
d = d >> options.delete(:months) if options[:months]
-
d = d + options.delete(:weeks) * 7 if options[:weeks]
-
d = d + options.delete(:days) if options[:days]
-
d
-
end
-
-
# Returns a new Date where one or more of the elements have been changed according to the +options+ parameter.
-
#
-
# Examples:
-
#
-
# Date.new(2007, 5, 12).change(:day => 1) # => Date.new(2007, 5, 1)
-
# Date.new(2007, 5, 12).change(:year => 2005, :month => 1) # => Date.new(2005, 1, 12)
-
1
def change(options)
-
::Date.new(
-
options[:year] || self.year,
-
options[:month] || self.month,
-
options[:day] || self.day
-
)
-
end
-
-
# Returns a new Date/DateTime representing the time a number of specified weeks ago.
-
1
def weeks_ago(weeks)
-
advance(:weeks => -weeks)
-
end
-
-
# Returns a new Date/DateTime representing the time a number of specified months ago.
-
1
def months_ago(months)
-
advance(:months => -months)
-
end
-
-
# Returns a new Date/DateTime representing the time a number of specified months in the future.
-
1
def months_since(months)
-
advance(:months => months)
-
end
-
-
# Returns a new Date/DateTime representing the time a number of specified years ago.
-
1
def years_ago(years)
-
advance(:years => -years)
-
end
-
-
# Returns a new Date/DateTime representing the time a number of specified years in the future.
-
1
def years_since(years)
-
advance(:years => years)
-
end
-
-
# Shorthand for years_ago(1)
-
def prev_year
-
years_ago(1)
-
1
end unless method_defined?(:prev_year)
-
-
# Shorthand for years_since(1)
-
def next_year
-
years_since(1)
-
1
end unless method_defined?(:next_year)
-
-
# Shorthand for months_ago(1)
-
def prev_month
-
months_ago(1)
-
1
end unless method_defined?(:prev_month)
-
-
# Shorthand for months_since(1)
-
def next_month
-
months_since(1)
-
1
end unless method_defined?(:next_month)
-
-
# Returns a new Date/DateTime representing the "start" of this week (i.e, Monday; DateTime objects will have time set to 0:00).
-
1
def beginning_of_week
-
days_to_monday = self.wday!=0 ? self.wday-1 : 6
-
result = self - days_to_monday
-
self.acts_like?(:time) ? result.midnight : result
-
end
-
1
alias :monday :beginning_of_week
-
1
alias :at_beginning_of_week :beginning_of_week
-
-
# Returns a new Date/DateTime representing the end of this week (Sunday, DateTime objects will have time set to 23:59:59).
-
1
def end_of_week
-
days_to_sunday = self.wday!=0 ? 7-self.wday : 0
-
result = self + days_to_sunday.days
-
self.acts_like?(:time) ? result.end_of_day : result
-
end
-
1
alias :sunday :end_of_week
-
1
alias :at_end_of_week :end_of_week
-
-
# Returns a new Date/DateTime representing the start of the given day in the previous week (default is Monday).
-
1
def prev_week(day = :monday)
-
result = (self - 7).beginning_of_week + DAYS_INTO_WEEK[day]
-
self.acts_like?(:time) ? result.change(:hour => 0) : result
-
end
-
-
# Returns a new Date/DateTime representing the start of the given day in next week (default is Monday).
-
1
def next_week(day = :monday)
-
result = (self + 7).beginning_of_week + DAYS_INTO_WEEK[day]
-
self.acts_like?(:time) ? result.change(:hour => 0) : result
-
end
-
-
# Returns a new ; DateTime objects will have time set to 0:00DateTime representing the start of the month (1st of the month; DateTime objects will have time set to 0:00)
-
1
def beginning_of_month
-
self.acts_like?(:time) ? change(:day => 1, :hour => 0) : change(:day => 1)
-
end
-
1
alias :at_beginning_of_month :beginning_of_month
-
-
# Returns a new Date/DateTime representing the end of the month (last day of the month; DateTime objects will have time set to 0:00)
-
1
def end_of_month
-
last_day = ::Time.days_in_month( self.month, self.year )
-
self.acts_like?(:time) ? change(:day => last_day, :hour => 23, :min => 59, :sec => 59) : change(:day => last_day)
-
end
-
1
alias :at_end_of_month :end_of_month
-
-
# Returns a new Date/DateTime representing the start of the quarter (1st of january, april, july, october; DateTime objects will have time set to 0:00)
-
1
def beginning_of_quarter
-
beginning_of_month.change(:month => [10, 7, 4, 1].detect { |m| m <= self.month })
-
end
-
1
alias :at_beginning_of_quarter :beginning_of_quarter
-
-
# Returns a new Date/DateTime representing the end of the quarter (last day of march, june, september, december; DateTime objects will have time set to 23:59:59)
-
1
def end_of_quarter
-
beginning_of_month.change(:month => [3, 6, 9, 12].detect { |m| m >= self.month }).end_of_month
-
end
-
1
alias :at_end_of_quarter :end_of_quarter
-
-
# Returns a new Date/DateTime representing the start of the year (1st of january; DateTime objects will have time set to 0:00)
-
1
def beginning_of_year
-
self.acts_like?(:time) ? change(:month => 1, :day => 1, :hour => 0) : change(:month => 1, :day => 1)
-
end
-
1
alias :at_beginning_of_year :beginning_of_year
-
-
# Returns a new Time representing the end of the year (31st of december; DateTime objects will have time set to 23:59:59)
-
1
def end_of_year
-
self.acts_like?(:time) ? change(:month => 12, :day => 31, :hour => 23, :min => 59, :sec => 59) : change(:month => 12, :day => 31)
-
end
-
1
alias :at_end_of_year :end_of_year
-
-
# Convenience method which returns a new Date/DateTime representing the time 1 day ago
-
1
def yesterday
-
self - 1
-
end
-
-
# Convenience method which returns a new Date/DateTime representing the time 1 day since the instance time
-
1
def tomorrow
-
self + 1
-
end
-
end
-
1
require 'date'
-
1
require 'active_support/inflector/methods'
-
1
require 'active_support/core_ext/date/zones'
-
1
require 'active_support/core_ext/module/remove_method'
-
-
1
class Date
-
1
DATE_FORMATS = {
-
:short => "%e %b",
-
:long => "%B %e, %Y",
-
:db => "%Y-%m-%d",
-
:number => "%Y%m%d",
-
:long_ordinal => lambda { |date| date.strftime("%B #{ActiveSupport::Inflector.ordinalize(date.day)}, %Y") }, # => "April 25th, 2007"
-
:rfc822 => "%e %b %Y"
-
}
-
-
# Ruby 1.9 has Date#to_time which converts to localtime only.
-
1
remove_possible_method :to_time
-
-
# Ruby 1.9 has Date#xmlschema which converts to a string without the time component.
-
1
remove_possible_method :xmlschema
-
-
# Convert to a formatted string. See DATE_FORMATS for predefined formats.
-
#
-
# This method is aliased to <tt>to_s</tt>.
-
#
-
# ==== Examples
-
# date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
-
#
-
# date.to_formatted_s(:db) # => "2007-11-10"
-
# date.to_s(:db) # => "2007-11-10"
-
#
-
# date.to_formatted_s(:short) # => "10 Nov"
-
# date.to_formatted_s(:long) # => "November 10, 2007"
-
# date.to_formatted_s(:long_ordinal) # => "November 10th, 2007"
-
# date.to_formatted_s(:rfc822) # => "10 Nov 2007"
-
#
-
# == Adding your own time formats to to_formatted_s
-
# You can add your own formats to the Date::DATE_FORMATS hash.
-
# Use the format name as the hash key and either a strftime string
-
# or Proc instance that takes a date argument as the value.
-
#
-
# # config/initializers/time_formats.rb
-
# Date::DATE_FORMATS[:month_and_year] = "%B %Y"
-
# Date::DATE_FORMATS[:short_ordinal] = lambda { |date| date.strftime("%B #{date.day.ordinalize}") }
-
1
def to_formatted_s(format = :default)
-
if formatter = DATE_FORMATS[format]
-
if formatter.respond_to?(:call)
-
formatter.call(self).to_s
-
else
-
strftime(formatter)
-
end
-
else
-
to_default_s
-
end
-
end
-
1
alias_method :to_default_s, :to_s
-
1
alias_method :to_s, :to_formatted_s
-
-
# Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005"
-
1
def readable_inspect
-
strftime("%a, %d %b %Y")
-
end
-
1
alias_method :default_inspect, :inspect
-
1
alias_method :inspect, :readable_inspect
-
-
# A method to keep Time, Date and DateTime instances interchangeable on conversions.
-
# In this case, it simply returns +self+.
-
def to_date
-
self
-
1
end if RUBY_VERSION < '1.9'
-
-
# Converts a Date instance to a Time, where the time is set to the beginning of the day.
-
# The timezone can be either :local or :utc (default :local).
-
#
-
# ==== Examples
-
# date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
-
#
-
# date.to_time # => Sat Nov 10 00:00:00 0800 2007
-
# date.to_time(:local) # => Sat Nov 10 00:00:00 0800 2007
-
#
-
# date.to_time(:utc) # => Sat Nov 10 00:00:00 UTC 2007
-
1
def to_time(form = :local)
-
::Time.send("#{form}_time", year, month, day)
-
end
-
-
# Converts a Date instance to a DateTime, where the time is set to the beginning of the day
-
# and UTC offset is set to 0.
-
#
-
# ==== Examples
-
# date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
-
#
-
# date.to_datetime # => Sat, 10 Nov 2007 00:00:00 0000
-
def to_datetime
-
::DateTime.civil(year, month, day, 0, 0, 0, 0)
-
1
end if RUBY_VERSION < '1.9'
-
-
def iso8601
-
strftime('%F')
-
1
end if RUBY_VERSION < '1.9'
-
-
1
alias_method :rfc3339, :iso8601 if RUBY_VERSION < '1.9'
-
-
1
def xmlschema
-
to_time_in_current_zone.xmlschema
-
end
-
end
-
# Date memoizes some instance methods using metaprogramming to wrap
-
# the methods with one that caches the result in an instance variable.
-
#
-
# If a Date is frozen but the memoized method hasn't been called, the
-
# first call will result in a frozen object error since the memo
-
# instance variable is uninitialized.
-
#
-
# Work around by eagerly memoizing before freezing.
-
#
-
# Ruby 1.9 uses a preinitialized instance variable so it's unaffected.
-
# This hack is as close as we can get to feature detection:
-
1
if RUBY_VERSION < '1.9'
-
require 'date'
-
begin
-
::Date.today.freeze.jd
-
rescue => frozen_object_error
-
if frozen_object_error.message =~ /frozen/
-
class Date #:nodoc:
-
def freeze
-
self.class.private_instance_methods(false).each do |m|
-
if m.to_s =~ /\A__\d+__\Z/
-
instance_variable_set(:"@#{m}", [send(m)])
-
end
-
end
-
-
super
-
end
-
end
-
end
-
end
-
end
-
1
require 'date'
-
1
require 'active_support/core_ext/time/zones'
-
-
1
class Date
-
# Converts Date to a TimeWithZone in the current zone if Time.zone or Time.zone_default
-
# is set, otherwise converts Date to a Time via Date#to_time
-
1
def to_time_in_current_zone
-
if ::Time.zone
-
::Time.zone.local(year, month, day)
-
else
-
to_time
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/acts_like'
-
-
1
class DateTime
-
# Duck-types as a Date-like class. See Object#acts_like?.
-
1
def acts_like_date?
-
true
-
end
-
-
# Duck-types as a Time-like class. See Object#acts_like?.
-
1
def acts_like_time?
-
true
-
end
-
end
-
1
require 'rational' unless RUBY_VERSION >= '1.9.2'
-
-
1
class DateTime
-
1
class << self
-
# DateTimes aren't aware of DST rules, so use a consistent non-DST offset when creating a DateTime with an offset in the local zone
-
1
def local_offset
-
::Time.local(2007).utc_offset.to_r / 86400
-
end
-
-
# Returns <tt>Time.zone.now.to_datetime</tt> when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise returns <tt>Time.now.to_datetime</tt>.
-
1
def current
-
::Time.zone ? ::Time.zone.now.to_datetime : ::Time.now.to_datetime
-
end
-
end
-
-
# Tells whether the DateTime object's datetime lies in the past
-
1
def past?
-
self < ::DateTime.current
-
end
-
-
# Tells whether the DateTime object's datetime lies in the future
-
1
def future?
-
self > ::DateTime.current
-
end
-
-
# Seconds since midnight: DateTime.now.seconds_since_midnight
-
1
def seconds_since_midnight
-
sec + (min * 60) + (hour * 3600)
-
end
-
-
# Returns a new DateTime where one or more of the elements have been changed according to the +options+ parameter. The time options
-
# (hour, minute, sec) reset cascadingly, so if only the hour is passed, then minute and sec is set to 0. If the hour and
-
# minute is passed, then sec is set to 0.
-
1
def change(options)
-
::DateTime.civil(
-
options[:year] || year,
-
options[:month] || month,
-
options[:day] || day,
-
options[:hour] || hour,
-
options[:min] || (options[:hour] ? 0 : min),
-
options[:sec] || ((options[:hour] || options[:min]) ? 0 : sec),
-
options[:offset] || offset,
-
options[:start] || start
-
)
-
end
-
-
# Uses Date to provide precise Time calculations for years, months, and days.
-
# The +options+ parameter takes a hash with any of these keys: <tt>:years</tt>,
-
# <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>, <tt>:hours</tt>,
-
# <tt>:minutes</tt>, <tt>:seconds</tt>.
-
1
def advance(options)
-
d = to_date.advance(options)
-
datetime_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day)
-
seconds_to_advance = (options[:seconds] || 0) + (options[:minutes] || 0) * 60 + (options[:hours] || 0) * 3600
-
seconds_to_advance == 0 ? datetime_advanced_by_date : datetime_advanced_by_date.since(seconds_to_advance)
-
end
-
-
# Returns a new DateTime representing the time a number of seconds ago
-
# Do not use this method in combination with x.months, use months_ago instead!
-
1
def ago(seconds)
-
since(-seconds)
-
end
-
-
# Returns a new DateTime representing the time a number of seconds since the instance time
-
# Do not use this method in combination with x.months, use months_since instead!
-
1
def since(seconds)
-
self + Rational(seconds.round, 86400)
-
end
-
1
alias :in :since
-
-
# Returns a new DateTime representing the start of the day (0:00)
-
1
def beginning_of_day
-
change(:hour => 0)
-
end
-
1
alias :midnight :beginning_of_day
-
1
alias :at_midnight :beginning_of_day
-
1
alias :at_beginning_of_day :beginning_of_day
-
-
# Returns a new DateTime representing the end of the day (23:59:59)
-
1
def end_of_day
-
change(:hour => 23, :min => 59, :sec => 59)
-
end
-
-
# 1.9.3 defines + and - on DateTime, < 1.9.3 do not.
-
1
if DateTime.public_instance_methods(false).include?(:+)
-
def plus_with_duration(other) #:nodoc:
-
if ActiveSupport::Duration === other
-
other.since(self)
-
else
-
plus_without_duration(other)
-
end
-
end
-
alias_method :plus_without_duration, :+
-
alias_method :+, :plus_with_duration
-
-
def minus_with_duration(other) #:nodoc:
-
if ActiveSupport::Duration === other
-
plus_with_duration(-other)
-
else
-
minus_without_duration(other)
-
end
-
end
-
alias_method :minus_without_duration, :-
-
alias_method :-, :minus_with_duration
-
end
-
-
# Adjusts DateTime to UTC by adding its offset value; offset is set to 0
-
#
-
# Example:
-
#
-
# DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)) # => Mon, 21 Feb 2005 10:11:12 -0600
-
# DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)).utc # => Mon, 21 Feb 2005 16:11:12 +0000
-
1
def utc
-
new_offset(0)
-
end
-
1
alias_method :getutc, :utc
-
-
# Returns true if offset == 0
-
1
def utc?
-
offset == 0
-
end
-
-
# Returns the offset value in seconds
-
1
def utc_offset
-
(offset * 86400).to_i
-
end
-
-
# Layers additional behavior on DateTime#<=> so that Time and ActiveSupport::TimeWithZone instances can be compared with a DateTime
-
1
def <=>(other)
-
super other.to_datetime
-
end
-
end
-
1
require 'active_support/inflector/methods'
-
1
require 'active_support/core_ext/time/conversions'
-
1
require 'active_support/core_ext/date_time/calculations'
-
1
require 'active_support/values/time_zone'
-
-
1
class DateTime
-
# Ruby 1.9 has DateTime#to_time which internally relies on Time. We define our own #to_time which allows
-
# DateTimes outside the range of what can be created with Time.
-
1
remove_method :to_time if instance_methods.include?(:to_time)
-
-
# Convert to a formatted string. See Time::DATE_FORMATS for predefined formats.
-
#
-
# This method is aliased to <tt>to_s</tt>.
-
#
-
# === Examples
-
# datetime = DateTime.civil(2007, 12, 4, 0, 0, 0, 0) # => Tue, 04 Dec 2007 00:00:00 +0000
-
#
-
# datetime.to_formatted_s(:db) # => "2007-12-04 00:00:00"
-
# datetime.to_s(:db) # => "2007-12-04 00:00:00"
-
# datetime.to_s(:number) # => "20071204000000"
-
# datetime.to_formatted_s(:short) # => "04 Dec 00:00"
-
# datetime.to_formatted_s(:long) # => "December 04, 2007 00:00"
-
# datetime.to_formatted_s(:long_ordinal) # => "December 4th, 2007 00:00"
-
# datetime.to_formatted_s(:rfc822) # => "Tue, 04 Dec 2007 00:00:00 +0000"
-
#
-
# == Adding your own datetime formats to to_formatted_s
-
# DateTime formats are shared with Time. You can add your own to the
-
# Time::DATE_FORMATS hash. Use the format name as the hash key and
-
# either a strftime string or Proc instance that takes a time or
-
# datetime argument as the value.
-
#
-
# # config/initializers/time_formats.rb
-
# Time::DATE_FORMATS[:month_and_year] = "%B %Y"
-
# Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") }
-
1
def to_formatted_s(format = :default)
-
if formatter = ::Time::DATE_FORMATS[format]
-
formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
-
else
-
to_default_s
-
end
-
end
-
1
alias_method :to_default_s, :to_s unless (instance_methods(false) & [:to_s, 'to_s']).empty?
-
1
alias_method :to_s, :to_formatted_s
-
-
# Returns the +utc_offset+ as an +HH:MM formatted string. Examples:
-
#
-
# datetime = DateTime.civil(2000, 1, 1, 0, 0, 0, Rational(-6, 24))
-
# datetime.formatted_offset # => "-06:00"
-
# datetime.formatted_offset(false) # => "-0600"
-
1
def formatted_offset(colon = true, alternate_utc_string = nil)
-
utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon)
-
end
-
-
# Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005 14:30:00 +0000".
-
1
def readable_inspect
-
to_s(:rfc822)
-
end
-
1
alias_method :default_inspect, :inspect
-
1
alias_method :inspect, :readable_inspect
-
-
# Converts self to a Ruby Date object; time portion is discarded.
-
def to_date
-
::Date.new(year, month, day)
-
1
end unless instance_methods(false).include?(:to_date)
-
-
# Attempts to convert self to a Ruby Time object; returns self if out of range of Ruby Time class.
-
# If self has an offset other than 0, self will just be returned unaltered, since there's no clean way to map it to a Time.
-
1
def to_time
-
self.offset == 0 ? ::Time.utc_time(year, month, day, hour, min, sec, sec_fraction * (RUBY_VERSION < '1.9' ? 86400000000 : 1000000)) : self
-
end
-
-
# To be able to keep Times, Dates and DateTimes interchangeable on conversions.
-
def to_datetime
-
self
-
1
end unless instance_methods(false).include?(:to_datetime)
-
-
1
def self.civil_from_format(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0)
-
offset = utc_or_local.to_sym == :local ? local_offset : 0
-
civil(year, month, day, hour, min, sec, offset)
-
end
-
-
# Converts datetime to an appropriate format for use in XML.
-
def xmlschema
-
strftime("%Y-%m-%dT%H:%M:%S%Z")
-
1
end unless instance_methods(false).include?(:xmlschema)
-
-
# Converts self to a floating-point number of seconds since the Unix epoch.
-
1
def to_f
-
seconds_since_unix_epoch.to_f
-
end
-
-
# Converts self to an integer number of seconds since the Unix epoch.
-
1
def to_i
-
seconds_since_unix_epoch.to_i
-
end
-
-
1
private
-
-
1
def seconds_since_unix_epoch
-
seconds_per_day = 86_400
-
(self - ::DateTime.civil(1970)) * seconds_per_day
-
end
-
end
-
1
require 'active_support/core_ext/time/zones'
-
-
1
class DateTime
-
# Returns the simultaneous time in <tt>Time.zone</tt>.
-
#
-
# Time.zone = 'Hawaii' # => 'Hawaii'
-
# DateTime.new(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00
-
#
-
# This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt> as the local zone
-
# instead of the operating system's time zone.
-
#
-
# You can also pass in a TimeZone instance or string that identifies a TimeZone as an argument,
-
# and the conversion will be based on that zone instead of <tt>Time.zone</tt>.
-
#
-
# DateTime.new(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
-
1
def in_time_zone(zone = ::Time.zone)
-
return self unless zone
-
-
ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.find_zone!(zone))
-
end
-
end
-
1
require 'active_support/ordered_hash'
-
-
1
module Enumerable
-
# Ruby 1.8.7 introduces group_by, but the result isn't ordered. Override it.
-
1
remove_method(:group_by) if [].respond_to?(:group_by) && RUBY_VERSION < '1.9'
-
-
# Collect an enumerable into sets, grouped by the result of a block. Useful,
-
# for example, for grouping records by date.
-
#
-
# Example:
-
#
-
# latest_transcripts.group_by(&:day).each do |day, transcripts|
-
# p "#{day} -> #{transcripts.map(&:class).join(', ')}"
-
# end
-
# "2006-03-01 -> Transcript"
-
# "2006-02-28 -> Transcript"
-
# "2006-02-27 -> Transcript, Transcript"
-
# "2006-02-26 -> Transcript, Transcript"
-
# "2006-02-25 -> Transcript"
-
# "2006-02-24 -> Transcript, Transcript"
-
# "2006-02-23 -> Transcript"
-
def group_by
-
assoc = ActiveSupport::OrderedHash.new
-
-
each do |element|
-
key = yield(element)
-
-
if assoc.has_key?(key)
-
assoc[key] << element
-
else
-
assoc[key] = [element]
-
end
-
end
-
-
assoc
-
1
end unless [].respond_to?(:group_by)
-
-
# Calculates a sum from the elements. Examples:
-
#
-
# payments.sum { |p| p.price * p.tax_rate }
-
# payments.sum(&:price)
-
#
-
# The latter is a shortcut for:
-
#
-
# payments.inject(0) { |sum, p| sum + p.price }
-
#
-
# It can also calculate the sum without the use of a block.
-
#
-
# [5, 15, 10].sum # => 30
-
# ["foo", "bar"].sum # => "foobar"
-
# [[1, 2], [3, 1, 5]].sum => [1, 2, 3, 1, 5]
-
#
-
# The default sum of an empty list is zero. You can override this default:
-
#
-
# [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
-
#
-
1
def sum(identity = 0, &block)
-
if block_given?
-
map(&block).sum(identity)
-
else
-
inject { |sum, element| sum + element } || identity
-
end
-
end
-
-
# Iterates over a collection, passing the current element *and* the
-
# +memo+ to the block. Handy for building up hashes or
-
# reducing collections down to one object. Examples:
-
#
-
# %w(foo bar).each_with_object({}) { |str, hsh| hsh[str] = str.upcase }
-
# # => {'foo' => 'FOO', 'bar' => 'BAR'}
-
#
-
# *Note* that you can't use immutable objects like numbers, true or false as
-
# the memo. You would think the following returns 120, but since the memo is
-
# never changed, it does not.
-
#
-
# (1..5).each_with_object(1) { |value, memo| memo *= value } # => 1
-
#
-
def each_with_object(memo, &block)
-
each do |element|
-
block.call(element, memo)
-
end
-
memo
-
1
end unless [].respond_to?(:each_with_object)
-
-
# Convert an enumerable to a hash. Examples:
-
#
-
# people.index_by(&:login)
-
# => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
-
# people.index_by { |person| "#{person.first_name} #{person.last_name}" }
-
# => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
-
#
-
1
def index_by
-
Hash[map { |elem| [yield(elem), elem] }]
-
end
-
-
# Returns true if the collection has more than 1 element. Functionally equivalent to collection.size > 1.
-
# Can be called with a block too, much like any?, so people.many? { |p| p.age > 26 } returns true if more than 1 person is over 26.
-
1
def many?(&block)
-
size = block_given? ? count(&block) : self.size
-
size > 1
-
end
-
-
# The negative of the Enumerable#include?. Returns true if the collection does not include the object.
-
1
def exclude?(object)
-
!include?(object)
-
end
-
end
-
-
1
class Range #:nodoc:
-
# Optimize range sum to use arithmetic progression if a block is not given and
-
# we have a range of numeric values.
-
1
def sum(identity = 0)
-
return super if block_given? || !(first.instance_of?(Integer) && last.instance_of?(Integer))
-
actual_last = exclude_end? ? (last - 1) : last
-
(actual_last - first + 1) * (actual_last + first) / 2
-
end
-
end
-
1
module ActiveSupport
-
1
FrozenObjectError = RUBY_VERSION < '1.9' ? TypeError : RuntimeError
-
end
-
1
require 'active_support/core_ext/file/atomic'
-
1
require 'active_support/core_ext/file/path'
-
1
class File
-
# Write to a file atomically. Useful for situations where you don't
-
# want other processes or threads to see half-written files.
-
#
-
# File.atomic_write("important.file") do |file|
-
# file.write("hello")
-
# end
-
#
-
# If your temp directory is not on the same filesystem as the file you're
-
# trying to write, you can provide a different temporary directory.
-
#
-
# File.atomic_write("/data/something.important", "/data/tmp") do |file|
-
# file.write("hello")
-
# end
-
1
def self.atomic_write(file_name, temp_dir = Dir.tmpdir)
-
require 'tempfile' unless defined?(Tempfile)
-
require 'fileutils' unless defined?(FileUtils)
-
-
temp_file = Tempfile.new(basename(file_name), temp_dir)
-
yield temp_file
-
temp_file.close
-
-
begin
-
# Get original file permissions
-
old_stat = stat(file_name)
-
rescue Errno::ENOENT
-
# No old permissions, write a temp file to determine the defaults
-
check_name = join(dirname(file_name), ".permissions_check.#{Thread.current.object_id}.#{Process.pid}.#{rand(1000000)}")
-
open(check_name, "w") { }
-
old_stat = stat(check_name)
-
unlink(check_name)
-
end
-
-
# Overwrite original file with temp file
-
FileUtils.mv(temp_file.path, file_name)
-
-
# Set correct permissions on new file
-
chown(old_stat.uid, old_stat.gid, file_name)
-
chmod(old_stat.mode, file_name)
-
end
-
end
-
1
class File
-
1
unless File.allocate.respond_to?(:to_path)
-
alias to_path path
-
end
-
end
-
1
require 'active_support/core_ext/float/rounding'
-
class Float
-
alias precisionless_round round
-
private :precisionless_round
-
-
# Rounds the float with the specified precision.
-
#
-
# x = 1.337
-
# x.round # => 1
-
# x.round(1) # => 1.3
-
# x.round(2) # => 1.34
-
def round(precision = nil)
-
if precision
-
magnitude = 10.0 ** precision
-
(self * magnitude).round / magnitude
-
else
-
precisionless_round
-
end
-
end
-
1
end if RUBY_VERSION < '1.9'
-
1
require 'active_support/core_ext/hash/conversions'
-
1
require 'active_support/core_ext/hash/deep_merge'
-
1
require 'active_support/core_ext/hash/deep_dup'
-
1
require 'active_support/core_ext/hash/diff'
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/core_ext/hash/reverse_merge'
-
1
require 'active_support/core_ext/hash/slice'
-
1
require 'active_support/xml_mini'
-
1
require 'active_support/time'
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_support/core_ext/hash/reverse_merge'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/string/inflections'
-
-
1
class Hash
-
# Returns a string containing an XML representation of its receiver:
-
#
-
# {"foo" => 1, "bar" => 2}.to_xml
-
# # =>
-
# # <?xml version="1.0" encoding="UTF-8"?>
-
# # <hash>
-
# # <foo type="integer">1</foo>
-
# # <bar type="integer">2</bar>
-
# # </hash>
-
#
-
# To do so, the method loops over the pairs and builds nodes that depend on
-
# the _values_. Given a pair +key+, +value+:
-
#
-
# * If +value+ is a hash there's a recursive call with +key+ as <tt>:root</tt>.
-
#
-
# * If +value+ is an array there's a recursive call with +key+ as <tt>:root</tt>,
-
# and +key+ singularized as <tt>:children</tt>.
-
#
-
# * If +value+ is a callable object it must expect one or two arguments. Depending
-
# on the arity, the callable is invoked with the +options+ hash as first argument
-
# with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. The
-
# callable can add nodes by using <tt>options[:builder]</tt>.
-
#
-
# "foo".to_xml(lambda { |options, key| options[:builder].b(key) })
-
# # => "<b>foo</b>"
-
#
-
# * If +value+ responds to +to_xml+ the method is invoked with +key+ as <tt>:root</tt>.
-
#
-
# class Foo
-
# def to_xml(options)
-
# options[:builder].bar "fooing!"
-
# end
-
# end
-
#
-
# {:foo => Foo.new}.to_xml(:skip_instruct => true)
-
# # => "<hash><bar>fooing!</bar></hash>"
-
#
-
# * Otherwise, a node with +key+ as tag is created with a string representation of
-
# +value+ as text node. If +value+ is +nil+ an attribute "nil" set to "true" is added.
-
# Unless the option <tt>:skip_types</tt> exists and is true, an attribute "type" is
-
# added as well according to the following mapping:
-
#
-
# XML_TYPE_NAMES = {
-
# "Symbol" => "symbol",
-
# "Fixnum" => "integer",
-
# "Bignum" => "integer",
-
# "BigDecimal" => "decimal",
-
# "Float" => "float",
-
# "TrueClass" => "boolean",
-
# "FalseClass" => "boolean",
-
# "Date" => "date",
-
# "DateTime" => "datetime",
-
# "Time" => "datetime"
-
# }
-
#
-
# By default the root node is "hash", but that's configurable via the <tt>:root</tt> option.
-
#
-
# The default XML builder is a fresh instance of <tt>Builder::XmlMarkup</tt>. You can
-
# configure your own builder with the <tt>:builder</tt> option. The method also accepts
-
# options like <tt>:dasherize</tt> and friends, they are forwarded to the builder.
-
1
def to_xml(options = {})
-
require 'active_support/builder' unless defined?(Builder)
-
-
options = options.dup
-
options[:indent] ||= 2
-
options[:root] ||= "hash"
-
options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
-
-
builder = options[:builder]
-
builder.instruct! unless options.delete(:skip_instruct)
-
-
root = ActiveSupport::XmlMini.rename_key(options[:root].to_s, options)
-
-
builder.__send__(:method_missing, root) do
-
each { |key, value| ActiveSupport::XmlMini.to_tag(key, value, options) }
-
yield builder if block_given?
-
end
-
end
-
-
1
class << self
-
1
def from_xml(xml)
-
typecast_xml_value(unrename_keys(ActiveSupport::XmlMini.parse(xml)))
-
end
-
-
1
private
-
1
def typecast_xml_value(value)
-
case value.class.to_s
-
when 'Hash'
-
if value['type'] == 'array'
-
_, entries = Array.wrap(value.detect { |k,v| k != 'type' })
-
if entries.nil? || (c = value['__content__'] && c.blank?)
-
[]
-
else
-
case entries.class.to_s # something weird with classes not matching here. maybe singleton methods breaking is_a?
-
when "Array"
-
entries.collect { |v| typecast_xml_value(v) }
-
when "Hash"
-
[typecast_xml_value(entries)]
-
else
-
raise "can't typecast #{entries.inspect}"
-
end
-
end
-
elsif value['type'] == 'file' ||
-
(value["__content__"] && (value.keys.size == 1 || value["__content__"].present?))
-
content = value["__content__"]
-
if parser = ActiveSupport::XmlMini::PARSING[value["type"]]
-
parser.arity == 1 ? parser.call(content) : parser.call(content, value)
-
else
-
content
-
end
-
elsif value['type'] == 'string' && value['nil'] != 'true'
-
""
-
# blank or nil parsed values are represented by nil
-
elsif value.blank? || value['nil'] == 'true'
-
nil
-
# If the type is the only element which makes it then
-
# this still makes the value nil, except if type is
-
# a XML node(where type['value'] is a Hash)
-
elsif value['type'] && value.size == 1 && !value['type'].is_a?(::Hash)
-
nil
-
else
-
xml_value = Hash[value.map { |k,v| [k, typecast_xml_value(v)] }]
-
-
# Turn { :files => { :file => #<StringIO> } into { :files => #<StringIO> } so it is compatible with
-
# how multipart uploaded files from HTML appear
-
xml_value["file"].is_a?(StringIO) ? xml_value["file"] : xml_value
-
end
-
when 'Array'
-
value.map! { |i| typecast_xml_value(i) }
-
value.length > 1 ? value : value.first
-
when 'String'
-
value
-
else
-
raise "can't typecast #{value.class.name} - #{value.inspect}"
-
end
-
end
-
-
1
def unrename_keys(params)
-
case params.class.to_s
-
when "Hash"
-
Hash[params.map { |k,v| [k.to_s.tr("-", "_"), unrename_keys(v)] } ]
-
when "Array"
-
params.map { |v| unrename_keys(v) }
-
else
-
params
-
end
-
end
-
end
-
end
-
1
class Hash
-
# Returns a new hash with +self+ and +other_hash+ merged recursively.
-
1
def deep_merge(other_hash)
-
dup.deep_merge!(other_hash)
-
end
-
-
# Returns a new hash with +self+ and +other_hash+ merged recursively.
-
# Modifies the receiver in place.
-
1
def deep_merge!(other_hash)
-
6
other_hash.each_pair do |k,v|
-
10
tv = self[k]
-
10
self[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? tv.deep_merge(v) : v
-
end
-
6
self
-
end
-
end
-
1
class Hash
-
# Returns a hash that represents the difference between two hashes.
-
#
-
# Examples:
-
#
-
# {1 => 2}.diff(1 => 2) # => {}
-
# {1 => 2}.diff(1 => 3) # => {1 => 2}
-
# {}.diff(1 => 2) # => {1 => 2}
-
# {1 => 2, 3 => 4}.diff(1 => 2) # => {3 => 4}
-
1
def diff(h2)
-
dup.delete_if { |k, v| h2[k] == v }.merge!(h2.dup.delete_if { |k, v| has_key?(k) })
-
end
-
end
-
1
class Hash
-
# Return a hash that includes everything but the given keys. This is useful for
-
# limiting a set of parameters to everything but a few known toggles:
-
#
-
# @person.update_attributes(params[:person].except(:admin))
-
#
-
# If the receiver responds to +convert_key+, the method is called on each of the
-
# arguments. This allows +except+ to play nice with hashes with indifferent access
-
# for instance:
-
#
-
# {:a => 1}.with_indifferent_access.except(:a) # => {}
-
# {:a => 1}.with_indifferent_access.except("a") # => {}
-
#
-
1
def except(*keys)
-
41
dup.except!(*keys)
-
end
-
-
# Replaces the hash without the given keys.
-
1
def except!(*keys)
-
155
keys.each { |key| delete(key) }
-
41
self
-
end
-
end
-
1
require 'active_support/hash_with_indifferent_access'
-
-
1
class Hash
-
-
# Returns an +ActiveSupport::HashWithIndifferentAccess+ out of its receiver:
-
#
-
# {:a => 1}.with_indifferent_access["a"] # => 1
-
#
-
1
def with_indifferent_access
-
ActiveSupport::HashWithIndifferentAccess.new_from_hash_copying_default(self)
-
end
-
-
# Called when object is nested under an object that receives
-
# #with_indifferent_access. This method will be called on the current object
-
# by the enclosing object and is aliased to #with_indifferent_access by
-
# default. Subclasses of Hash may overwrite this method to return +self+ if
-
# converting to an +ActiveSupport::HashWithIndifferentAccess+ would not be
-
# desirable.
-
#
-
# b = {:b => 1}
-
# {:a => b}.with_indifferent_access["a"] # calls b.nested_under_indifferent_access
-
#
-
1
alias nested_under_indifferent_access with_indifferent_access
-
end
-
1
class Hash
-
# Slice a hash to include only the given keys. This is useful for
-
# limiting an options hash to valid keys before passing to a method:
-
#
-
# def search(criteria = {})
-
# assert_valid_keys(:mass, :velocity, :time)
-
# end
-
#
-
# search(options.slice(:mass, :velocity, :time))
-
#
-
# If you have an array of keys you want to limit to, you should splat them:
-
#
-
# valid_keys = [:mass, :velocity, :time]
-
# search(options.slice(*valid_keys))
-
1
def slice(*keys)
-
25
keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key)
-
25
hash = self.class.new
-
88
keys.each { |k| hash[k] = self[k] if has_key?(k) }
-
25
hash
-
end
-
-
# Replaces the hash with only the given keys.
-
# Returns a hash contained the removed key/value pairs
-
# {:a => 1, :b => 2, :c => 3, :d => 4}.slice!(:a, :b) # => {:c => 3, :d => 4}
-
1
def slice!(*keys)
-
10
keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key)
-
10
omit = slice(*self.keys - keys)
-
10
hash = slice(*keys)
-
10
replace(hash)
-
10
omit
-
end
-
-
# Removes and returns the key/value pairs matching the given keys.
-
# {:a => 1, :b => 2, :c => 3, :d => 4}.extract!(:a, :b) # => {:a => 1, :b => 2}
-
1
def extract!(*keys)
-
result = {}
-
keys.each {|key| result[key] = delete(key) }
-
result
-
end
-
end
-
1
require 'active_support/core_ext/integer/multiple'
-
1
require 'active_support/core_ext/integer/inflections'
-
1
require 'active_support/core_ext/integer/time'
-
1
require 'active_support/inflector'
-
-
1
class Integer
-
# Ordinalize turns a number into an ordinal string used to denote the
-
# position in an ordered sequence such as 1st, 2nd, 3rd, 4th.
-
#
-
# 1.ordinalize # => "1st"
-
# 2.ordinalize # => "2nd"
-
# 1002.ordinalize # => "1002nd"
-
# 1003.ordinalize # => "1003rd"
-
# -11.ordinalize # => "-11th"
-
# -1001.ordinalize # => "-1001st"
-
#
-
1
def ordinalize
-
ActiveSupport::Inflector.ordinalize(self)
-
end
-
end
-
1
class Integer
-
# Check whether the integer is evenly divisible by the argument.
-
1
def multiple_of?(number)
-
number != 0 ? self % number == 0 : zero?
-
end
-
end
-
1
class Integer
-
# Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years.
-
#
-
# These methods use Time#advance for precise date calculations when using from_now, ago, etc.
-
# as well as adding or subtracting their results from a Time object. For example:
-
#
-
# # equivalent to Time.now.advance(:months => 1)
-
# 1.month.from_now
-
#
-
# # equivalent to Time.now.advance(:years => 2)
-
# 2.years.from_now
-
#
-
# # equivalent to Time.now.advance(:months => 4, :years => 5)
-
# (4.months + 5.years).from_now
-
#
-
# While these methods provide precise calculation when used as in the examples above, care
-
# should be taken to note that this is not true if the result of `months', `years', etc is
-
# converted before use:
-
#
-
# # equivalent to 30.days.to_i.from_now
-
# 1.month.to_i.from_now
-
#
-
# # equivalent to 365.25.days.to_f.from_now
-
# 1.year.to_f.from_now
-
#
-
# In such cases, Ruby's core
-
# Date[http://stdlib.rubyonrails.org/libdoc/date/rdoc/index.html] and
-
# Time[http://stdlib.rubyonrails.org/libdoc/time/rdoc/index.html] should be used for precision
-
# date and time arithmetic
-
1
def months
-
ActiveSupport::Duration.new(self * 30.days, [[:months, self]])
-
end
-
1
alias :month :months
-
-
1
def years
-
ActiveSupport::Duration.new(self * 365.25.days, [[:years, self]])
-
end
-
1
alias :year :years
-
end
-
1
require 'active_support/core_ext/kernel/reporting'
-
1
require 'active_support/core_ext/kernel/agnostics'
-
1
require 'active_support/core_ext/kernel/requires'
-
1
require 'active_support/core_ext/kernel/debugger'
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
1
class Object
-
# Makes backticks behave (somewhat more) similarly on all platforms.
-
# On win32 `nonexistent_command` raises Errno::ENOENT; on Unix, the
-
# spawned shell prints a message to stderr and sets $?. We emulate
-
# Unix on the former but not the latter.
-
1
def `(command) #:nodoc:
-
super
-
rescue Errno::ENOENT => e
-
STDERR.puts "#$0: #{e}"
-
end
-
end
-
1
module Kernel
-
1
unless respond_to?(:debugger)
-
# Starts a debugging session if ruby-debug has been loaded (call rails server --debugger to do load it).
-
1
def debugger
-
message = "\n***** Debugger requested, but was not available (ensure ruby-debug is listed in Gemfile/installed as gem): Start server with --debugger to enable *****\n"
-
defined?(Rails) ? Rails.logger.info(message) : $stderr.puts(message)
-
end
-
end
-
-
1
undef :breakpoint if respond_to?(:breakpoint)
-
1
def breakpoint
-
message = "\n***** The 'breakpoint' command has been renamed 'debugger' -- please change *****\n"
-
defined?(Rails) ? Rails.logger.info(message) : $stderr.puts(message)
-
debugger
-
end
-
end
-
1
require 'active_support/core_ext/kernel/reporting'
-
1
require 'active_support/core_ext/module/deprecation'
-
-
1
module Kernel
-
# Require a library with fallback to RubyGems. Warnings during library
-
# loading are silenced to increase signal/noise for application warnings.
-
1
def require_library_or_gem(library_name)
-
silence_warnings do
-
begin
-
require library_name
-
rescue LoadError => cannot_require
-
# 1. Requiring the module is unsuccessful, maybe it's a gem and nobody required rubygems yet. Try.
-
begin
-
require 'rubygems'
-
rescue LoadError # => rubygems_not_installed
-
raise cannot_require
-
end
-
# 2. Rubygems is installed and loaded. Try to load the library again
-
begin
-
require library_name
-
rescue LoadError # => gem_not_installed
-
raise cannot_require
-
end
-
end
-
end
-
end
-
1
deprecate :require_library_or_gem
-
end
-
1
module Kernel
-
# Returns the object's singleton class.
-
def singleton_class
-
class << self
-
self
-
end
-
1
end unless respond_to?(:singleton_class) # exists in 1.9.2
-
-
# class_eval on an object acts like singleton_class.class_eval.
-
1
def class_eval(*args, &block)
-
singleton_class.class_eval(*args, &block)
-
end
-
end
-
1
class LoadError
-
1
REGEXPS = [
-
/^no such file to load -- (.+)$/i,
-
/^Missing \w+ (?:file\s*)?([^\s]+.rb)$/i,
-
/^Missing API definition file in (.+)$/i,
-
/^cannot load such file -- (.+)$/i,
-
]
-
-
1
def path
-
@path ||= begin
-
REGEXPS.find do |regex|
-
message =~ regex
-
end
-
$1
-
end
-
end
-
-
1
def is_missing?(location)
-
location.sub(/\.rb$/, '') == path.sub(/\.rb$/, '')
-
end
-
end
-
-
1
MissingSourceFile = LoadError
-
1
require 'active_support/core_ext/module/aliasing'
-
1
require 'active_support/core_ext/module/introspection'
-
1
require 'active_support/core_ext/module/anonymous'
-
1
require 'active_support/core_ext/module/reachable'
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'active_support/core_ext/module/attr_internal'
-
1
require 'active_support/core_ext/module/attr_accessor_with_default'
-
1
require 'active_support/core_ext/module/delegation'
-
1
require 'active_support/core_ext/module/synchronization'
-
1
require 'active_support/core_ext/module/deprecation'
-
1
require 'active_support/core_ext/module/remove_method'
-
1
require 'active_support/core_ext/module/method_names'
-
1
require 'active_support/core_ext/object/blank'
-
-
1
class Module
-
# A module may or may not have a name.
-
#
-
# module M; end
-
# M.name # => "M"
-
#
-
# m = Module.new
-
# m.name # => ""
-
#
-
# A module gets a name when it is first assigned to a constant. Either
-
# via the +module+ or +class+ keyword or by an explicit assignment:
-
#
-
# m = Module.new # creates an anonymous module
-
# M = m # => m gets a name here as a side-effect
-
# m.name # => "M"
-
#
-
1
def anonymous?
-
# Uses blank? because the name of an anonymous class is an empty
-
# string in 1.8, and nil in 1.9.
-
30
name.blank?
-
end
-
end
-
1
class Module
-
# Declare an attribute accessor with an initial default return value.
-
#
-
# To give attribute <tt>:age</tt> the initial value <tt>25</tt>:
-
#
-
# class Person
-
# attr_accessor_with_default :age, 25
-
# end
-
#
-
# person = Person.new
-
# person.age # => 25
-
#
-
# person.age = 26
-
# person.age # => 26
-
#
-
# To give attribute <tt>:element_name</tt> a dynamic default value, evaluated
-
# in scope of self:
-
#
-
# attr_accessor_with_default(:element_name) { name.underscore }
-
#
-
1
def attr_accessor_with_default(sym, default = Proc.new)
-
ActiveSupport::Deprecation.warn "attr_accessor_with_default is deprecated. Use Ruby instead!"
-
define_method(sym, block_given? ? default : Proc.new { default })
-
module_eval(<<-EVAL, __FILE__, __LINE__ + 1)
-
def #{sym}=(value) # def age=(value)
-
class << self; attr_accessor :#{sym} end # class << self; attr_accessor :age end
-
@#{sym} = value # @age = value
-
end # end
-
EVAL
-
end
-
end
-
1
class Module
-
# Declares an attribute reader backed by an internally-named instance variable.
-
1
def attr_internal_reader(*attrs)
-
22
attrs.each {|attr_name| attr_internal_define(attr_name, :reader)}
-
end
-
-
# Declares an attribute writer backed by an internally-named instance variable.
-
1
def attr_internal_writer(*attrs)
-
28
attrs.each {|attr_name| attr_internal_define(attr_name, :writer)}
-
end
-
-
# Declares an attribute reader and writer backed by an internally-named instance
-
# variable.
-
1
def attr_internal_accessor(*attrs)
-
9
attr_internal_reader(*attrs)
-
9
attr_internal_writer(*attrs)
-
end
-
-
1
alias_method :attr_internal, :attr_internal_accessor
-
-
2
class << self; attr_accessor :attr_internal_naming_format end
-
1
self.attr_internal_naming_format = '@_%s'
-
-
1
private
-
1
def attr_internal_ivar_name(attr)
-
29
Module.attr_internal_naming_format % attr
-
end
-
-
1
def attr_internal_define(attr_name, type)
-
29
internal_name = attr_internal_ivar_name(attr_name).sub(/\A@/, '')
-
29
class_eval do # class_eval is necessary on 1.9 or else the methods a made private
-
# use native attr_* methods as they are faster on some Ruby implementations
-
29
send("attr_#{type}", internal_name)
-
end
-
29
attr_name, internal_name = "#{attr_name}=", "#{internal_name}=" if type == :writer
-
29
alias_method attr_name, internal_name
-
29
remove_method internal_name
-
end
-
end
-
1
require 'active_support/inflector'
-
-
1
class Module
-
# Returns the name of the module containing this one.
-
#
-
# M::N.parent_name # => "M"
-
1
def parent_name
-
26
unless defined? @parent_name
-
9
@parent_name = name =~ /::[^:]+\Z/ ? $`.freeze : nil
-
end
-
26
@parent_name
-
end
-
-
# Returns the module which contains this one according to its name.
-
#
-
# module M
-
# module N
-
# end
-
# end
-
# X = M::N
-
#
-
# M::N.parent # => M
-
# X.parent # => M
-
#
-
# The parent of top-level and anonymous modules is Object.
-
#
-
# M.parent # => Object
-
# Module.new.parent # => Object
-
#
-
1
def parent
-
10
parent_name ? ActiveSupport::Inflector.constantize(parent_name) : Object
-
end
-
-
# Returns all the parents of this module according to its name, ordered from
-
# nested outwards. The receiver is not contained within the result.
-
#
-
# module M
-
# module N
-
# end
-
# end
-
# X = M::N
-
#
-
# M.parents # => [Object]
-
# M::N.parents # => [M, Object]
-
# X.parents # => [M, Object]
-
#
-
1
def parents
-
14
parents = []
-
14
if parent_name
-
1
parts = parent_name.split('::')
-
1
until parts.empty?
-
1
parents << ActiveSupport::Inflector.constantize(parts * '::')
-
1
parts.pop
-
end
-
end
-
14
parents << Object unless parents.include? Object
-
14
parents
-
end
-
-
1
if RUBY_VERSION < '1.9'
-
# Returns the constants that have been defined locally by this object and
-
# not in an ancestor. This method is exact if running under Ruby 1.9. In
-
# previous versions it may miss some constants if their definition in some
-
# ancestor is identical to their definition in the receiver.
-
def local_constants
-
inherited = {}
-
-
ancestors.each do |anc|
-
next if anc == self
-
anc.constants.each { |const| inherited[const] = anc.const_get(const) }
-
end
-
-
constants.select do |const|
-
!inherited.key?(const) || inherited[const].object_id != const_get(const).object_id
-
end
-
end
-
else
-
1
def local_constants #:nodoc:
-
486
constants(false)
-
end
-
end
-
-
# Returns the names of the constants defined locally rather than the
-
# constants themselves. See <tt>local_constants</tt>.
-
1
def local_constant_names
-
107648
local_constants.map { |c| c.to_s }
-
end
-
end
-
1
class Module
-
1
if instance_methods[0].is_a?(Symbol)
-
1
def instance_method_names(*args)
-
1
instance_methods(*args).map(&:to_s)
-
end
-
-
1
def method_names(*args)
-
methods(*args).map(&:to_s)
-
end
-
else
-
alias_method :instance_method_names, :instance_methods
-
alias_method :method_names, :methods
-
end
-
end
-
1
require 'active_support/core_ext/module/anonymous'
-
1
require 'active_support/core_ext/string/inflections'
-
-
1
class Module
-
1
def reachable? #:nodoc:
-
!anonymous? && name.constantize.equal?(self)
-
rescue NameError
-
false
-
end
-
end
-
1
require 'thread'
-
1
require 'active_support/core_ext/module/aliasing'
-
1
require 'active_support/core_ext/array/extract_options'
-
-
1
class Module
-
# Synchronize access around a method, delegating synchronization to a
-
# particular mutex. A mutex (either a Mutex, or any object that responds to
-
# #synchronize and yields to a block) must be provided as a final :with option.
-
# The :with option should be a symbol or string, and can represent a method,
-
# constant, or instance or class variable.
-
# Example:
-
# class SharedCache
-
# @@lock = Mutex.new
-
# def expire
-
# ...
-
# end
-
# synchronize :expire, :with => :@@lock
-
# end
-
1
def synchronize(*methods)
-
1
options = methods.extract_options!
-
1
unless options.is_a?(Hash) && with = options[:with]
-
raise ArgumentError, "Synchronization needs a mutex. Supply an options hash with a :with key as the last argument (e.g. synchronize :hello, :with => :@mutex)."
-
end
-
-
1
methods.each do |method|
-
4
aliased_method, punctuation = method.to_s.sub(/([?!=])$/, ''), $1
-
-
4
if method_defined?("#{aliased_method}_without_synchronization#{punctuation}")
-
raise ArgumentError, "#{method} is already synchronized. Double synchronization is not currently supported."
-
end
-
-
4
module_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
def #{aliased_method}_with_synchronization#{punctuation}(*args, &block) # def expire_with_synchronization(*args, &block)
-
#{with}.synchronize do # @@lock.synchronize do
-
#{aliased_method}_without_synchronization#{punctuation}(*args, &block) # expire_without_synchronization(*args, &block)
-
end # end
-
end # end
-
EOS
-
-
4
alias_method_chain method, :synchronization
-
end
-
end
-
end
-
1
class NameError
-
# Extract the name of the missing constant from the exception message.
-
1
def missing_name
-
3
if /undefined local variable or method/ !~ message
-
3
$1 if /((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/ =~ message
-
end
-
end
-
-
# Was this exception raised because the given name was missing?
-
1
def missing_name?(name)
-
3
if name.is_a? Symbol
-
last_name = (missing_name || '').split('::').last
-
last_name == name.to_s
-
else
-
3
missing_name == name.to_s
-
end
-
end
-
end
-
1
require 'active_support/core_ext/numeric/bytes'
-
1
require 'active_support/core_ext/numeric/time'
-
1
class Numeric
-
1
KILOBYTE = 1024
-
1
MEGABYTE = KILOBYTE * 1024
-
1
GIGABYTE = MEGABYTE * 1024
-
1
TERABYTE = GIGABYTE * 1024
-
1
PETABYTE = TERABYTE * 1024
-
1
EXABYTE = PETABYTE * 1024
-
-
# Enables the use of byte calculations and declarations, like 45.bytes + 2.6.megabytes
-
1
def bytes
-
self
-
end
-
1
alias :byte :bytes
-
-
1
def kilobytes
-
1
self * KILOBYTE
-
end
-
1
alias :kilobyte :kilobytes
-
-
1
def megabytes
-
self * MEGABYTE
-
end
-
1
alias :megabyte :megabytes
-
-
1
def gigabytes
-
self * GIGABYTE
-
end
-
1
alias :gigabyte :gigabytes
-
-
1
def terabytes
-
self * TERABYTE
-
end
-
1
alias :terabyte :terabytes
-
-
1
def petabytes
-
self * PETABYTE
-
end
-
1
alias :petabyte :petabytes
-
-
1
def exabytes
-
self * EXABYTE
-
end
-
1
alias :exabyte :exabytes
-
end
-
1
require 'active_support/duration'
-
1
require 'active_support/core_ext/time/calculations'
-
1
require 'active_support/core_ext/time/acts_like'
-
-
1
class Numeric
-
# Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years.
-
#
-
# These methods use Time#advance for precise date calculations when using from_now, ago, etc.
-
# as well as adding or subtracting their results from a Time object. For example:
-
#
-
# # equivalent to Time.now.advance(:months => 1)
-
# 1.month.from_now
-
#
-
# # equivalent to Time.now.advance(:years => 2)
-
# 2.years.from_now
-
#
-
# # equivalent to Time.now.advance(:months => 4, :years => 5)
-
# (4.months + 5.years).from_now
-
#
-
# While these methods provide precise calculation when used as in the examples above, care
-
# should be taken to note that this is not true if the result of `months', `years', etc is
-
# converted before use:
-
#
-
# # equivalent to 30.days.to_i.from_now
-
# 1.month.to_i.from_now
-
#
-
# # equivalent to 365.25.days.to_f.from_now
-
# 1.year.to_f.from_now
-
#
-
# In such cases, Ruby's core
-
# Date[http://stdlib.rubyonrails.org/libdoc/date/rdoc/index.html] and
-
# Time[http://stdlib.rubyonrails.org/libdoc/time/rdoc/index.html] should be used for precision
-
# date and time arithmetic
-
1
def seconds
-
ActiveSupport::Duration.new(self, [[:seconds, self]])
-
end
-
1
alias :second :seconds
-
-
1
def minutes
-
ActiveSupport::Duration.new(self * 60, [[:seconds, self * 60]])
-
end
-
1
alias :minute :minutes
-
-
1
def hours
-
ActiveSupport::Duration.new(self * 3600, [[:seconds, self * 3600]])
-
end
-
1
alias :hour :hours
-
-
1
def days
-
ActiveSupport::Duration.new(self * 24.hours, [[:days, self]])
-
end
-
1
alias :day :days
-
-
1
def weeks
-
ActiveSupport::Duration.new(self * 7.days, [[:days, self * 7]])
-
end
-
1
alias :week :weeks
-
-
1
def fortnights
-
ActiveSupport::Duration.new(self * 2.weeks, [[:days, self * 14]])
-
end
-
1
alias :fortnight :fortnights
-
-
# Reads best without arguments: 10.minutes.ago
-
1
def ago(time = ::Time.current)
-
time - self
-
end
-
-
# Reads best with argument: 10.minutes.until(time)
-
1
alias :until :ago
-
-
# Reads best with argument: 10.minutes.since(time)
-
1
def since(time = ::Time.current)
-
time + self
-
end
-
-
# Reads best without arguments: 10.minutes.from_now
-
1
alias :from_now :since
-
end
-
1
require 'active_support/core_ext/object/acts_like'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/object/duplicable'
-
1
require 'active_support/core_ext/object/try'
-
1
require 'active_support/core_ext/object/inclusion'
-
-
1
require 'active_support/core_ext/object/conversions'
-
1
require 'active_support/core_ext/object/instance_variables'
-
-
1
require 'active_support/core_ext/object/to_json'
-
1
require 'active_support/core_ext/object/to_param'
-
1
require 'active_support/core_ext/object/to_query'
-
1
require 'active_support/core_ext/object/with_options'
-
1
class Object
-
# A duck-type assistant method. For example, Active Support extends Date
-
# to define an acts_like_date? method, and extends Time to define
-
# acts_like_time?. As a result, we can do "x.acts_like?(:time)" and
-
# "x.acts_like?(:date)" to do duck-type-safe comparisons, since classes that
-
# we want to act like Time simply need to define an acts_like_time? method.
-
1
def acts_like?(duck)
-
respond_to? :"acts_like_#{duck}?"
-
end
-
end
-
1
require 'active_support/core_ext/object/to_param'
-
1
require 'active_support/core_ext/object/to_query'
-
1
require 'active_support/core_ext/array/conversions'
-
1
require 'active_support/core_ext/hash/conversions'
-
#--
-
# Most objects are cloneable, but not all. For example you can't dup +nil+:
-
#
-
# nil.dup # => TypeError: can't dup NilClass
-
#
-
# Classes may signal their instances are not duplicable removing +dup+/+clone+
-
# or raising exceptions from them. So, to dup an arbitrary object you normally
-
# use an optimistic approach and are ready to catch an exception, say:
-
#
-
# arbitrary_object.dup rescue object
-
#
-
# Rails dups objects in a few critical spots where they are not that arbitrary.
-
# That rescue is very expensive (like 40 times slower than a predicate), and it
-
# is often triggered.
-
#
-
# That's why we hardcode the following cases and check duplicable? instead of
-
# using that rescue idiom.
-
#++
-
1
class Object
-
# Can you safely dup this object?
-
#
-
# False for +nil+, +false+, +true+, symbols, numbers, class and module objects;
-
# true otherwise.
-
1
def duplicable?
-
true
-
end
-
end
-
-
1
class NilClass
-
# +nil+ is not duplicable:
-
#
-
# nil.duplicable? # => false
-
# nil.dup # => TypeError: can't dup NilClass
-
#
-
1
def duplicable?
-
9
false
-
end
-
end
-
-
1
class FalseClass
-
# +false+ is not duplicable:
-
#
-
# false.duplicable? # => false
-
# false.dup # => TypeError: can't dup FalseClass
-
#
-
1
def duplicable?
-
false
-
end
-
end
-
-
1
class TrueClass
-
# +true+ is not duplicable:
-
#
-
# true.duplicable? # => false
-
# true.dup # => TypeError: can't dup TrueClass
-
#
-
1
def duplicable?
-
false
-
end
-
end
-
-
1
class Symbol
-
# Symbols are not duplicable:
-
#
-
# :my_symbol.duplicable? # => false
-
# :my_symbol.dup # => TypeError: can't dup Symbol
-
#
-
1
def duplicable?
-
false
-
end
-
end
-
-
1
class Numeric
-
# Numbers are not duplicable:
-
#
-
# 3.duplicable? # => false
-
# 3.dup # => TypeError: can't dup Fixnum
-
#
-
1
def duplicable?
-
false
-
end
-
end
-
-
1
class Class
-
# Classes are not duplicable:
-
#
-
# c = Class.new # => #<Class:0x10328fd80>
-
# c.dup # => #<Class:0x10328fd80>
-
#
-
# Note +dup+ returned the same class object.
-
1
def duplicable?
-
false
-
end
-
end
-
-
1
class Module
-
# Modules are not duplicable:
-
#
-
# m = Module.new # => #<Module:0x10328b6e0>
-
# m.dup # => #<Module:0x10328b6e0>
-
#
-
# Note +dup+ returned the same module object.
-
1
def duplicable?
-
false
-
end
-
end
-
1
class Object
-
# Returns true if this object is included in the argument. Argument must be
-
# any object which responds to +#include?+. Usage:
-
#
-
# characters = ["Konata", "Kagami", "Tsukasa"]
-
# "Konata".in?(characters) # => true
-
#
-
# This will throw an ArgumentError if the argument doesn't respond
-
# to +#include?+.
-
1
def in?(another_object)
-
104
another_object.include?(self)
-
rescue NoMethodError
-
raise ArgumentError.new("The parameter passed to #in? must respond to #include?")
-
end
-
end
-
1
class Object
-
# Returns a hash that maps instance variable names without "@" to their
-
# corresponding values. Keys are strings both in Ruby 1.8 and 1.9.
-
#
-
# class C
-
# def initialize(x, y)
-
# @x, @y = x, y
-
# end
-
# end
-
#
-
# C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
-
1
def instance_values #:nodoc:
-
Hash[instance_variables.map { |name| [name.to_s[1..-1], instance_variable_get(name)] }]
-
end
-
-
# Returns an array of instance variable names including "@". They are strings
-
# both in Ruby 1.8 and 1.9.
-
#
-
# class C
-
# def initialize(x, y)
-
# @x, @y = x, y
-
# end
-
# end
-
#
-
# C.new(0, 1).instance_variable_names # => ["@y", "@x"]
-
1
if RUBY_VERSION >= '1.9'
-
1
def instance_variable_names
-
instance_variables.map { |var| var.to_s }
-
end
-
else
-
alias_method :instance_variable_names, :instance_variables
-
end
-
end
-
# Hack to load json gem first so we can overwrite its to_json.
-
1
begin
-
1
require 'json'
-
rescue LoadError
-
end
-
-
# The JSON gem adds a few modules to Ruby core classes containing :to_json definition, overwriting
-
# their default behavior. That said, we need to define the basic to_json method in all of them,
-
# otherwise they will always use to_json gem implementation, which is backwards incompatible in
-
# several cases (for instance, the JSON implementation for Hash does not work) with inheritance
-
# and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json.
-
1
[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass|
-
9
klass.class_eval <<-RUBY, __FILE__, __LINE__
-
# Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
-
def to_json(options = nil)
-
ActiveSupport::JSON.encode(self, options)
-
end
-
RUBY
-
end
-
1
class Object
-
# Alias of <tt>to_s</tt>.
-
1
def to_param
-
to_s
-
end
-
end
-
-
1
class NilClass
-
1
def to_param
-
self
-
end
-
end
-
-
1
class TrueClass
-
1
def to_param
-
self
-
end
-
end
-
-
1
class FalseClass
-
1
def to_param
-
self
-
end
-
end
-
-
1
class Array
-
# Calls <tt>to_param</tt> on all its elements and joins the result with
-
# slashes. This is used by <tt>url_for</tt> in Action Pack.
-
1
def to_param
-
collect { |e| e.to_param }.join '/'
-
end
-
end
-
-
1
class Hash
-
# Returns a string representation of the receiver suitable for use as a URL
-
# query string:
-
#
-
# {:name => 'David', :nationality => 'Danish'}.to_param
-
# # => "name=David&nationality=Danish"
-
#
-
# An optional namespace can be passed to enclose the param names:
-
#
-
# {:name => 'David', :nationality => 'Danish'}.to_param('user')
-
# # => "user[name]=David&user[nationality]=Danish"
-
#
-
# The string pairs "key=value" that conform the query string
-
# are sorted lexicographically in ascending order.
-
#
-
# This method is also aliased as +to_query+.
-
1
def to_param(namespace = nil)
-
collect do |key, value|
-
value.to_query(namespace ? "#{namespace}[#{key}]" : key)
-
end.sort * '&'
-
end
-
end
-
1
require 'active_support/core_ext/object/to_param'
-
-
1
class Object
-
# Converts an object into a string suitable for use as a URL query string, using the given <tt>key</tt> as the
-
# param name.
-
#
-
# Note: This method is defined as a default implementation for all Objects for Hash#to_query to work.
-
1
def to_query(key)
-
require 'cgi' unless defined?(CGI) && defined?(CGI::escape)
-
"#{CGI.escape(key.to_s)}=#{CGI.escape(to_param.to_s)}"
-
end
-
end
-
-
1
class Array
-
# Converts an array into a string suitable for use as a URL query string,
-
# using the given +key+ as the param name.
-
#
-
# ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"
-
1
def to_query(key)
-
prefix = "#{key}[]"
-
collect { |value| value.to_query(prefix) }.join '&'
-
end
-
end
-
-
1
class Hash
-
1
alias_method :to_query, :to_param
-
end
-
1
class Object
-
# Invokes the method identified by the symbol +method+, passing it any arguments
-
# and/or the block specified, just like the regular Ruby <tt>Object#send</tt> does.
-
#
-
# *Unlike* that method however, a +NoMethodError+ exception will *not* be raised
-
# and +nil+ will be returned instead, if the receiving object is a +nil+ object or NilClass.
-
#
-
# If try is called without a method to call, it will yield any given block with the object.
-
#
-
# ==== Examples
-
#
-
# Without +try+
-
# @person && @person.name
-
# or
-
# @person ? @person.name : nil
-
#
-
# With +try+
-
# @person.try(:name)
-
#
-
# +try+ also accepts arguments and/or a block, for the method it is trying
-
# Person.try(:find, 1)
-
# @people.try(:collect) {|p| p.name}
-
#
-
# Without a method argument try will yield to the block unless the receiver is nil.
-
# @person.try { |p| "#{p.first_name} #{p.last_name}" }
-
#--
-
# +try+ behaves like +Object#send+, unless called on +NilClass+.
-
1
def try(*a, &b)
-
if a.empty? && block_given?
-
yield self
-
elsif !a.empty? && !respond_to?(a.first)
-
nil
-
else
-
__send__(*a, &b)
-
end
-
end
-
end
-
-
1
class NilClass
-
# Calling +try+ on +nil+ always returns +nil+.
-
# It becomes specially helpful when navigating through associations that may return +nil+.
-
#
-
# === Examples
-
#
-
# nil.try(:name) # => nil
-
#
-
# Without +try+
-
# @person && !@person.children.blank? && @person.children.first.name
-
#
-
# With +try+
-
# @person.try(:children).try(:first).try(:name)
-
1
def try(*args)
-
nil
-
end
-
end
-
1
require 'active_support/option_merger'
-
-
1
class Object
-
# An elegant way to factor duplication out of options passed to a series of
-
# method calls. Each method called in the block, with the block variable as
-
# the receiver, will have its options merged with the default +options+ hash
-
# provided. Each method called on the block variable must take an options
-
# hash as its final argument.
-
#
-
# Without <tt>with_options></tt>, this code contains duplication:
-
#
-
# class Account < ActiveRecord::Base
-
# has_many :customers, :dependent => :destroy
-
# has_many :products, :dependent => :destroy
-
# has_many :invoices, :dependent => :destroy
-
# has_many :expenses, :dependent => :destroy
-
# end
-
#
-
# Using <tt>with_options</tt>, we can remove the duplication:
-
#
-
# class Account < ActiveRecord::Base
-
# with_options :dependent => :destroy do |assoc|
-
# assoc.has_many :customers
-
# assoc.has_many :products
-
# assoc.has_many :invoices
-
# assoc.has_many :expenses
-
# end
-
# end
-
#
-
# It can also be used with an explicit receiver:
-
#
-
# I18n.with_options :locale => user.locale, :scope => "newsletter" do |i18n|
-
# subject i18n.t :subject
-
# body i18n.t :body, :user_name => user.name
-
# end
-
#
-
# <tt>with_options</tt> can also be nested since the call is forwarded to its receiver.
-
# Each nesting level will merge inherited defaults in addition to their own.
-
#
-
1
def with_options(options)
-
yield ActiveSupport::OptionMerger.new(self, options)
-
end
-
end
-
1
require "active_support/core_ext/kernel/singleton_class"
-
-
1
class Proc #:nodoc:
-
1
def bind(object)
-
block, time = self, Time.now
-
object.class_eval do
-
method_name = "__bind_#{time.to_i}_#{time.usec}"
-
define_method(method_name, &block)
-
method = instance_method(method_name)
-
remove_method(method_name)
-
method
-
end.bind(object)
-
end
-
end
-
1
require 'active_support/core_ext/process/daemon'
-
1
module Process
-
def self.daemon(nochdir = nil, noclose = nil)
-
exit if fork # Parent exits, child continues.
-
Process.setsid # Become session leader.
-
exit if fork # Zap session leader. See [1].
-
-
unless nochdir
-
Dir.chdir "/" # Release old working directory.
-
end
-
-
File.umask 0000 # Ensure sensible umask. Adjust as needed.
-
-
unless noclose
-
STDIN.reopen "/dev/null" # Free file descriptors and
-
STDOUT.reopen "/dev/null", "a" # point them somewhere sensible.
-
STDERR.reopen '/dev/null', 'a'
-
end
-
-
trap("TERM") { exit }
-
-
return 0
-
1
end unless respond_to?(:daemon)
-
end
-
1
require 'active_support/core_ext/range/blockless_step'
-
1
require 'active_support/core_ext/range/conversions'
-
1
require 'active_support/core_ext/range/include_range'
-
1
require 'active_support/core_ext/range/overlaps'
-
1
require 'active_support/core_ext/range/cover'
-
1
require 'active_support/core_ext/module/aliasing'
-
-
1
class Range
-
1
begin
-
1
(1..2).step
-
# Range#step doesn't return an Enumerator
-
rescue LocalJumpError
-
# Return an array when step is called without a block.
-
def step_with_blockless(*args, &block)
-
if block_given?
-
step_without_blockless(*args, &block)
-
else
-
array = []
-
step_without_blockless(*args) { |step| array << step }
-
array
-
end
-
end
-
else
-
1
def step_with_blockless(*args, &block) #:nodoc:
-
if block_given?
-
step_without_blockless(*args, &block)
-
else
-
step_without_blockless(*args).to_a
-
end
-
end
-
end
-
-
1
alias_method_chain :step, :blockless
-
end
-
1
class Range
-
1
RANGE_FORMATS = {
-
:db => Proc.new { |start, stop| "BETWEEN '#{start.to_s(:db)}' AND '#{stop.to_s(:db)}'" }
-
}
-
-
# Gives a human readable format of the range.
-
#
-
# ==== Example
-
#
-
# [1..100].to_formatted_s # => "1..100"
-
1
def to_formatted_s(format = :default)
-
if formatter = RANGE_FORMATS[format]
-
formatter.call(first, last)
-
else
-
to_default_s
-
end
-
end
-
-
1
alias_method :to_default_s, :to_s
-
1
alias_method :to_s, :to_formatted_s
-
end
-
1
class Range
-
1
alias_method(:cover?, :include?) unless instance_methods.include?(:cover?)
-
end
-
1
class Range
-
# Extends the default Range#include? to support range comparisons.
-
# (1..5).include?(1..5) # => true
-
# (1..5).include?(2..3) # => true
-
# (1..5).include?(2..6) # => false
-
#
-
# The native Range#include? behavior is untouched.
-
# ("a".."f").include?("c") # => true
-
# (5..9).include?(11) # => false
-
1
def include_with_range?(value)
-
if value.is_a?(::Range)
-
operator = exclude_end? ? :< : :<=
-
end_value = value.exclude_end? ? last.succ : last
-
include_without_range?(value.first) && (value.last <=> end_value).send(operator, 0)
-
else
-
include_without_range?(value)
-
end
-
end
-
-
1
alias_method_chain :include?, :range
-
end
-
1
class Range
-
# Compare two ranges and see if they overlap each other
-
# (1..5).overlaps?(4..6) # => true
-
# (1..5).overlaps?(7..9) # => false
-
1
def overlaps?(other)
-
include?(other.first) || other.include?(first)
-
end
-
end
-
1
class Regexp #:nodoc:
-
1
def multiline?
-
options & MULTILINE == MULTILINE
-
end
-
end
-
1
require 'active_support/core_ext/kernel/reporting'
-
-
# Fixes the rexml vulnerability disclosed at:
-
# http://www.ruby-lang.org/en/news/2008/08/23/dos-vulnerability-in-rexml/
-
# This fix is identical to rexml-expansion-fix version 1.0.1.
-
#
-
# We still need to distribute this fix because albeit the REXML
-
# in recent 1.8.7s is patched, it wasn't in early patchlevels.
-
1
require 'rexml/rexml'
-
-
# Earlier versions of rexml defined REXML::Version, newer ones REXML::VERSION
-
1
unless (defined?(REXML::VERSION) ? REXML::VERSION : REXML::Version) > "3.1.7.2"
-
silence_warnings { require 'rexml/document' }
-
-
# REXML in 1.8.7 has the patch but early patchlevels didn't update Version from 3.1.7.2.
-
unless REXML::Document.respond_to?(:entity_expansion_limit=)
-
silence_warnings { require 'rexml/entity' }
-
-
module REXML #:nodoc:
-
class Entity < Child #:nodoc:
-
undef_method :unnormalized
-
def unnormalized
-
document.record_entity_expansion! if document
-
v = value()
-
return nil if v.nil?
-
@unnormalized = Text::unnormalize(v, parent)
-
@unnormalized
-
end
-
end
-
class Document < Element #:nodoc:
-
@@entity_expansion_limit = 10_000
-
def self.entity_expansion_limit= val
-
@@entity_expansion_limit = val
-
end
-
-
def record_entity_expansion!
-
@number_of_expansions ||= 0
-
@number_of_expansions += 1
-
if @number_of_expansions > @@entity_expansion_limit
-
raise "Number of entity expansions exceeded, processing aborted."
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/string/conversions'
-
1
require 'active_support/core_ext/string/filters'
-
1
require 'active_support/core_ext/string/multibyte'
-
1
require 'active_support/core_ext/string/starts_ends_with'
-
1
require 'active_support/core_ext/string/inflections'
-
1
require 'active_support/core_ext/string/access'
-
1
require 'active_support/core_ext/string/xchar'
-
1
require 'active_support/core_ext/string/behavior'
-
1
require 'active_support/core_ext/string/interpolation'
-
1
require 'active_support/core_ext/string/output_safety'
-
1
require 'active_support/core_ext/string/exclude'
-
1
require 'active_support/core_ext/string/encoding'
-
1
require 'active_support/core_ext/string/strip'
-
1
require 'active_support/core_ext/string/inquiry'
-
1
require "active_support/multibyte"
-
-
1
class String
-
1
unless '1.9'.respond_to?(:force_encoding)
-
# Returns the character at the +position+ treating the string as an array (where 0 is the first character).
-
#
-
# Examples:
-
# "hello".at(0) # => "h"
-
# "hello".at(4) # => "o"
-
# "hello".at(10) # => ERROR if < 1.9, nil in 1.9
-
def at(position)
-
mb_chars[position, 1].to_s
-
end
-
-
# Returns the remaining of the string from the +position+ treating the string as an array (where 0 is the first character).
-
#
-
# Examples:
-
# "hello".from(0) # => "hello"
-
# "hello".from(2) # => "llo"
-
# "hello".from(10) # => "" if < 1.9, nil in 1.9
-
def from(position)
-
mb_chars[position..-1].to_s
-
end
-
-
# Returns the beginning of the string up to the +position+ treating the string as an array (where 0 is the first character).
-
#
-
# Examples:
-
# "hello".to(0) # => "h"
-
# "hello".to(2) # => "hel"
-
# "hello".to(10) # => "hello"
-
def to(position)
-
mb_chars[0..position].to_s
-
end
-
-
# Returns the first character of the string or the first +limit+ characters.
-
#
-
# Examples:
-
# "hello".first # => "h"
-
# "hello".first(2) # => "he"
-
# "hello".first(10) # => "hello"
-
def first(limit = 1)
-
if limit == 0
-
''
-
elsif limit >= size
-
self
-
else
-
mb_chars[0...limit].to_s
-
end
-
end
-
-
# Returns the last character of the string or the last +limit+ characters.
-
#
-
# Examples:
-
# "hello".last # => "o"
-
# "hello".last(2) # => "lo"
-
# "hello".last(10) # => "hello"
-
def last(limit = 1)
-
if limit == 0
-
''
-
elsif limit >= size
-
self
-
else
-
mb_chars[(-limit)..-1].to_s
-
end
-
end
-
else
-
1
def at(position)
-
self[position]
-
end
-
-
1
def from(position)
-
self[position..-1]
-
end
-
-
1
def to(position)
-
self[0..position]
-
end
-
-
1
def first(limit = 1)
-
if limit == 0
-
''
-
elsif limit >= size
-
self
-
else
-
to(limit - 1)
-
end
-
end
-
-
1
def last(limit = 1)
-
if limit == 0
-
''
-
elsif limit >= size
-
self
-
else
-
from(-limit)
-
end
-
end
-
end
-
end
-
1
class String
-
# Enable more predictable duck-typing on String-like classes. See <tt>Object#acts_like?</tt>.
-
1
def acts_like_string?
-
true
-
end
-
end
-
# encoding: utf-8
-
1
require 'date'
-
1
require 'active_support/core_ext/time/publicize_conversion_methods'
-
1
require 'active_support/core_ext/time/calculations'
-
-
1
class String
-
# Returns the codepoint of the first character of the string, assuming a
-
# single-byte character encoding:
-
#
-
# "a".ord # => 97
-
# "à".ord # => 224, in ISO-8859-1
-
#
-
# This method is defined in Ruby 1.8 for Ruby 1.9 forward compatibility on
-
# these character encodings.
-
#
-
# <tt>ActiveSupport::Multibyte::Chars#ord</tt> is forward compatible with
-
# Ruby 1.9 on UTF8 strings:
-
#
-
# "a".mb_chars.ord # => 97
-
# "à".mb_chars.ord # => 224, in UTF8
-
#
-
# Note that the 224 is different in both examples. In ISO-8859-1 "à" is
-
# represented as a single byte, 224. In UTF8 it is represented with two
-
# bytes, namely 195 and 160, but its Unicode codepoint is 224. If we
-
# call +ord+ on the UTF8 string "à" the return value will be 195. That is
-
# not an error, because UTF8 is unsupported, the call itself would be
-
# bogus.
-
def ord
-
self[0]
-
1
end unless method_defined?(:ord)
-
-
# +getbyte+ backport from Ruby 1.9
-
1
alias_method :getbyte, :[] unless method_defined?(:getbyte)
-
-
# Form can be either :utc (default) or :local.
-
1
def to_time(form = :utc)
-
return nil if self.blank?
-
d = ::Date._parse(self, false).values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction).map { |arg| arg || 0 }
-
d[6] *= 1000000
-
::Time.send("#{form}_time", *d)
-
end
-
-
1
def to_date
-
return nil if self.blank?
-
::Date.new(*::Date._parse(self, false).values_at(:year, :mon, :mday))
-
end
-
-
1
def to_datetime
-
return nil if self.blank?
-
d = ::Date._parse(self, false).values_at(:year, :mon, :mday, :hour, :min, :sec, :zone, :sec_fraction).map { |arg| arg || 0 }
-
d[5] += d.pop
-
::DateTime.civil(*d)
-
end
-
end
-
1
class String
-
1
if defined?(Encoding) && "".respond_to?(:encode)
-
1
def encoding_aware?
-
2
true
-
end
-
else
-
def encoding_aware?
-
false
-
end
-
end
-
end
-
1
class String
-
# The inverse of <tt>String#include?</tt>. Returns true if the string does not include the other string.
-
1
def exclude?(string)
-
!include?(string)
-
end
-
end
-
1
require 'active_support/core_ext/string/multibyte'
-
-
1
class String
-
# Returns the string, first removing all whitespace on both ends of
-
# the string, and then changing remaining consecutive whitespace
-
# groups into one space each.
-
#
-
# Examples:
-
# %{ Multi-line
-
# string }.squish # => "Multi-line string"
-
# " foo bar \n \t boo".squish # => "foo bar boo"
-
1
def squish
-
dup.squish!
-
end
-
-
# Performs a destructive squish. See String#squish.
-
1
def squish!
-
strip!
-
gsub!(/\s+/, ' ')
-
self
-
end
-
-
# Truncates a given +text+ after a given <tt>length</tt> if +text+ is longer than <tt>length</tt>:
-
#
-
# "Once upon a time in a world far far away".truncate(27)
-
# # => "Once upon a time in a wo..."
-
#
-
# Pass a <tt>:separator</tt> to truncate +text+ at a natural break:
-
#
-
# "Once upon a time in a world far far away".truncate(27, :separator => ' ')
-
# # => "Once upon a time in a..."
-
#
-
# The last characters will be replaced with the <tt>:omission</tt> string (defaults to "...")
-
# for a total length not exceeding <tt>:length</tt>:
-
#
-
# "And they found that many people were sleeping better.".truncate(25, :omission => "... (continued)")
-
# # => "And they f... (continued)"
-
1
def truncate(length, options = {})
-
text = self.dup
-
options[:omission] ||= "..."
-
-
length_with_room_for_omission = length - options[:omission].mb_chars.length
-
chars = text.mb_chars
-
stop = options[:separator] ?
-
(chars.rindex(options[:separator].mb_chars, length_with_room_for_omission) || length_with_room_for_omission) : length_with_room_for_omission
-
-
(chars.length > length ? chars[0...stop] + options[:omission] : text).to_s
-
end
-
end
-
1
require 'active_support/string_inquirer'
-
-
1
class String
-
# Wraps the current string in the <tt>ActiveSupport::StringInquirer</tt> class,
-
# which gives you a prettier way to test for equality. Example:
-
#
-
# env = "production".inquiry
-
# env.production? # => true
-
# env.development? # => false
-
1
def inquiry
-
ActiveSupport::StringInquirer.new(self)
-
end
-
end
-
1
require 'active_support/i18n'
-
1
require 'i18n/core_ext/string/interpolate'
-
1
require 'erb'
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
-
1
class ERB
-
1
module Util
-
1
HTML_ESCAPE = { '&' => '&', '>' => '>', '<' => '<', '"' => '"' }
-
1
JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C' }
-
-
# A utility method for escaping HTML tag characters.
-
# This method is also aliased as <tt>h</tt>.
-
#
-
# In your ERB templates, use this method to escape any unsafe content. For example:
-
# <%=h @person.name %>
-
#
-
# ==== Example:
-
# puts html_escape("is a > 0 & a < 10?")
-
# # => is a > 0 & a < 10?
-
1
def html_escape(s)
-
s = s.to_s
-
if s.html_safe?
-
s
-
else
-
s.to_s.gsub(/&/, "&").gsub(/\"/, """).gsub(/>/, ">").gsub(/</, "<").html_safe
-
end
-
end
-
-
# Aliasing twice issues a warning "discarding old...". Remove first to avoid it.
-
1
remove_method(:h)
-
1
alias h html_escape
-
-
1
module_function :h
-
-
1
singleton_class.send(:remove_method, :html_escape)
-
1
module_function :html_escape
-
-
# A utility method for escaping HTML entities in JSON strings
-
# using \uXXXX JavaScript escape sequences for string literals:
-
#
-
# json_escape("is a > 0 & a < 10?")
-
# # => is a \u003E 0 \u0026 a \u003C 10?
-
#
-
# Note that after this operation is performed the output is not
-
# valid JSON. In particular double quotes are removed:
-
#
-
# json_escape('{"name":"john","created_at":"2010-04-28T01:39:31Z","id":1}')
-
# # => {name:john,created_at:2010-04-28T01:39:31Z,id:1}
-
#
-
# This method is also aliased as +j+, and available as a helper
-
# in Rails templates:
-
#
-
# <%=j @person.to_json %>
-
#
-
1
def json_escape(s)
-
result = s.to_s.gsub(/[&"><]/) { |special| JSON_ESCAPE[special] }
-
s.html_safe? ? result.html_safe : result
-
end
-
-
1
alias j json_escape
-
1
module_function :j
-
1
module_function :json_escape
-
end
-
end
-
-
1
class Object
-
1
def html_safe?
-
false
-
end
-
end
-
-
1
class Numeric
-
1
def html_safe?
-
true
-
end
-
end
-
-
1
module ActiveSupport #:nodoc:
-
1
class SafeBuffer < String
-
1
UNSAFE_STRING_METHODS = ["capitalize", "chomp", "chop", "delete", "downcase", "gsub", "lstrip", "next", "reverse", "rstrip", "slice", "squeeze", "strip", "sub", "succ", "swapcase", "tr", "tr_s", "upcase"].freeze
-
-
1
alias_method :original_concat, :concat
-
1
private :original_concat
-
-
1
class SafeConcatError < StandardError
-
1
def initialize
-
super "Could not concatenate to the buffer because it is not html safe."
-
end
-
end
-
-
1
def safe_concat(value)
-
raise SafeConcatError if dirty?
-
original_concat(value)
-
end
-
-
1
def initialize(*)
-
@dirty = false
-
super
-
end
-
-
1
def initialize_copy(other)
-
super
-
@dirty = other.dirty?
-
end
-
-
1
def concat(value)
-
if dirty? || value.html_safe?
-
super(value)
-
else
-
super(ERB::Util.h(value))
-
end
-
end
-
1
alias << concat
-
-
1
def +(other)
-
dup.concat(other)
-
end
-
-
1
def html_safe?
-
!dirty?
-
end
-
-
1
def to_s
-
self
-
end
-
-
1
def to_param
-
to_str
-
end
-
-
1
def encode_with(coder)
-
coder.represent_scalar nil, to_str
-
end
-
-
1
def to_yaml(*args)
-
return super() if defined?(YAML::ENGINE) && !YAML::ENGINE.syck?
-
to_str.to_yaml(*args)
-
end
-
-
1
for unsafe_method in UNSAFE_STRING_METHODS
-
19
class_eval <<-EOT, __FILE__, __LINE__
-
def #{unsafe_method}(*args)
-
super.to_str
-
end
-
-
def #{unsafe_method}!(*args)
-
@dirty = true
-
super
-
end
-
EOT
-
end
-
-
1
protected
-
-
1
def dirty?
-
@dirty
-
end
-
end
-
end
-
-
1
class String
-
1
def html_safe
-
ActiveSupport::SafeBuffer.new(self)
-
end
-
end
-
1
class String
-
1
alias_method :starts_with?, :start_with?
-
1
alias_method :ends_with?, :end_with?
-
end
-
1
require 'active_support/core_ext/object/try'
-
-
1
class String
-
# Strips indentation in heredocs.
-
#
-
# For example in
-
#
-
# if options[:usage]
-
# puts <<-USAGE.strip_heredoc
-
# This command does such and such.
-
#
-
# Supported options are:
-
# -h This message
-
# ...
-
# USAGE
-
# end
-
#
-
# the user would see the usage message aligned against the left margin.
-
#
-
# Technically, it looks for the least indented line in the whole string, and removes
-
# that amount of leading whitespace.
-
1
def strip_heredoc
-
indent = scan(/^[ \t]*(?=\S)/).min.try(:size) || 0
-
gsub(/^[ \t]{#{indent}}/, '')
-
end
-
end
-
1
begin
-
# See http://fast-xs.rubyforge.org/ by Eric Wong.
-
# Also included with hpricot.
-
1
require 'fast_xs'
-
rescue LoadError
-
# fast_xs extension unavailable
-
else
-
begin
-
require 'builder'
-
rescue LoadError
-
# builder demands the first shot at defining String#to_xs
-
end
-
-
class String
-
alias_method :original_xs, :to_xs if method_defined?(:to_xs)
-
alias_method :to_xs, :fast_xs
-
end
-
end
-
1
require 'active_support/core_ext/object/acts_like'
-
-
1
class Time
-
# Duck-types as a Time-like class. See Object#acts_like?.
-
1
def acts_like_time?
-
true
-
end
-
end
-
1
require 'active_support/duration'
-
1
require 'active_support/core_ext/time/zones'
-
1
require 'active_support/core_ext/time/conversions'
-
-
1
class Time
-
1
COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
-
1
DAYS_INTO_WEEK = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6 }
-
-
1
class << self
-
# Overriding case equality method so that it returns true for ActiveSupport::TimeWithZone instances
-
1
def ===(other)
-
other.is_a?(::Time)
-
end
-
-
# Return the number of days in the given month.
-
# If no year is specified, it will use the current year.
-
1
def days_in_month(month, year = now.year)
-
return 29 if month == 2 && ::Date.gregorian_leap?(year)
-
COMMON_YEAR_DAYS_IN_MONTH[month]
-
end
-
-
# Returns a new Time if requested year can be accommodated by Ruby's Time class
-
# (i.e., if year is within either 1970..2038 or 1902..2038, depending on system architecture);
-
# otherwise returns a DateTime.
-
1
def time_with_datetime_fallback(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0, usec=0)
-
time = ::Time.send(utc_or_local, year, month, day, hour, min, sec, usec)
-
# This check is needed because Time.utc(y) returns a time object in the 2000s for 0 <= y <= 138.
-
time.year == year ? time : ::DateTime.civil_from_format(utc_or_local, year, month, day, hour, min, sec)
-
rescue
-
::DateTime.civil_from_format(utc_or_local, year, month, day, hour, min, sec)
-
end
-
-
# Wraps class method +time_with_datetime_fallback+ with +utc_or_local+ set to <tt>:utc</tt>.
-
1
def utc_time(*args)
-
time_with_datetime_fallback(:utc, *args)
-
end
-
-
# Wraps class method +time_with_datetime_fallback+ with +utc_or_local+ set to <tt>:local</tt>.
-
1
def local_time(*args)
-
time_with_datetime_fallback(:local, *args)
-
end
-
-
# Returns <tt>Time.zone.now</tt> when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise just returns <tt>Time.now</tt>.
-
1
def current
-
::Time.zone ? ::Time.zone.now : ::Time.now
-
end
-
end
-
-
# Tells whether the Time object's time lies in the past
-
1
def past?
-
self < ::Time.current
-
end
-
-
# Tells whether the Time object's time is today
-
1
def today?
-
to_date == ::Date.current
-
end
-
-
# Tells whether the Time object's time lies in the future
-
1
def future?
-
self > ::Time.current
-
end
-
-
# Seconds since midnight: Time.now.seconds_since_midnight
-
1
def seconds_since_midnight
-
to_i - change(:hour => 0).to_i + (usec / 1.0e+6)
-
end
-
-
# Returns a new Time where one or more of the elements have been changed according to the +options+ parameter. The time options
-
# (hour, minute, sec, usec) reset cascadingly, so if only the hour is passed, then minute, sec, and usec is set to 0. If the hour and
-
# minute is passed, then sec and usec is set to 0.
-
1
def change(options)
-
::Time.send(
-
utc? ? :utc_time : :local_time,
-
options[:year] || year,
-
options[:month] || month,
-
options[:day] || day,
-
options[:hour] || hour,
-
options[:min] || (options[:hour] ? 0 : min),
-
options[:sec] || ((options[:hour] || options[:min]) ? 0 : sec),
-
options[:usec] || ((options[:hour] || options[:min] || options[:sec]) ? 0 : usec)
-
)
-
end
-
-
# Uses Date to provide precise Time calculations for years, months, and days.
-
# The +options+ parameter takes a hash with any of these keys: <tt>:years</tt>,
-
# <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>, <tt>:hours</tt>,
-
# <tt>:minutes</tt>, <tt>:seconds</tt>.
-
1
def advance(options)
-
unless options[:weeks].nil?
-
options[:weeks], partial_weeks = options[:weeks].divmod(1)
-
options[:days] = (options[:days] || 0) + 7 * partial_weeks
-
end
-
-
unless options[:days].nil?
-
options[:days], partial_days = options[:days].divmod(1)
-
options[:hours] = (options[:hours] || 0) + 24 * partial_days
-
end
-
-
d = to_date.advance(options)
-
time_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day)
-
seconds_to_advance = (options[:seconds] || 0) + (options[:minutes] || 0) * 60 + (options[:hours] || 0) * 3600
-
seconds_to_advance == 0 ? time_advanced_by_date : time_advanced_by_date.since(seconds_to_advance)
-
end
-
-
# Returns a new Time representing the time a number of seconds ago, this is basically a wrapper around the Numeric extension
-
1
def ago(seconds)
-
since(-seconds)
-
end
-
-
# Returns a new Time representing the time a number of seconds since the instance time
-
1
def since(seconds)
-
self + seconds
-
rescue
-
to_datetime.since(seconds)
-
end
-
1
alias :in :since
-
-
# Returns a new Time representing the time a number of specified weeks ago.
-
1
def weeks_ago(weeks)
-
advance(:weeks => -weeks)
-
end
-
-
# Returns a new Time representing the time a number of specified months ago
-
1
def months_ago(months)
-
advance(:months => -months)
-
end
-
-
# Returns a new Time representing the time a number of specified months in the future
-
1
def months_since(months)
-
advance(:months => months)
-
end
-
-
# Returns a new Time representing the time a number of specified years ago
-
1
def years_ago(years)
-
advance(:years => -years)
-
end
-
-
# Returns a new Time representing the time a number of specified years in the future
-
1
def years_since(years)
-
advance(:years => years)
-
end
-
-
# Short-hand for years_ago(1)
-
1
def prev_year
-
years_ago(1)
-
end
-
-
# Short-hand for years_since(1)
-
1
def next_year
-
years_since(1)
-
end
-
-
# Short-hand for months_ago(1)
-
1
def prev_month
-
months_ago(1)
-
end
-
-
# Short-hand for months_since(1)
-
1
def next_month
-
months_since(1)
-
end
-
-
# Returns a new Time representing the "start" of this week (Monday, 0:00)
-
1
def beginning_of_week
-
days_to_monday = wday!=0 ? wday-1 : 6
-
(self - days_to_monday.days).midnight
-
end
-
1
alias :monday :beginning_of_week
-
1
alias :at_beginning_of_week :beginning_of_week
-
-
# Returns a new Time representing the end of this week, (end of Sunday)
-
1
def end_of_week
-
days_to_sunday = wday!=0 ? 7-wday : 0
-
(self + days_to_sunday.days).end_of_day
-
end
-
1
alias :at_end_of_week :end_of_week
-
-
# Returns a new Time representing the start of the given day in the previous week (default is Monday).
-
1
def prev_week(day = :monday)
-
ago(1.week).beginning_of_week.since(DAYS_INTO_WEEK[day].day).change(:hour => 0)
-
end
-
-
# Returns a new Time representing the start of the given day in next week (default is Monday).
-
1
def next_week(day = :monday)
-
since(1.week).beginning_of_week.since(DAYS_INTO_WEEK[day].day).change(:hour => 0)
-
end
-
-
# Returns a new Time representing the start of the day (0:00)
-
1
def beginning_of_day
-
#(self - seconds_since_midnight).change(:usec => 0)
-
change(:hour => 0)
-
end
-
1
alias :midnight :beginning_of_day
-
1
alias :at_midnight :beginning_of_day
-
1
alias :at_beginning_of_day :beginning_of_day
-
-
# Returns a new Time representing the end of the day, 23:59:59.999999 (.999999999 in ruby1.9)
-
1
def end_of_day
-
change(:hour => 23, :min => 59, :sec => 59, :usec => 999999.999)
-
end
-
-
# Returns a new Time representing the start of the month (1st of the month, 0:00)
-
1
def beginning_of_month
-
#self - ((self.mday-1).days + self.seconds_since_midnight)
-
change(:day => 1, :hour => 0)
-
end
-
1
alias :at_beginning_of_month :beginning_of_month
-
-
# Returns a new Time representing the end of the month (end of the last day of the month)
-
1
def end_of_month
-
#self - ((self.mday-1).days + self.seconds_since_midnight)
-
last_day = ::Time.days_in_month(month, year)
-
change(:day => last_day, :hour => 23, :min => 59, :sec => 59, :usec => 999999.999)
-
end
-
1
alias :at_end_of_month :end_of_month
-
-
# Returns a new Time representing the start of the quarter (1st of january, april, july, october, 0:00)
-
1
def beginning_of_quarter
-
beginning_of_month.change(:month => [10, 7, 4, 1].detect { |m| m <= month })
-
end
-
1
alias :at_beginning_of_quarter :beginning_of_quarter
-
-
# Returns a new Time representing the end of the quarter (end of the last day of march, june, september, december)
-
1
def end_of_quarter
-
beginning_of_month.change(:month => [3, 6, 9, 12].detect { |m| m >= month }).end_of_month
-
end
-
1
alias :at_end_of_quarter :end_of_quarter
-
-
# Returns a new Time representing the start of the year (1st of january, 0:00)
-
1
def beginning_of_year
-
change(:month => 1, :day => 1, :hour => 0)
-
end
-
1
alias :at_beginning_of_year :beginning_of_year
-
-
# Returns a new Time representing the end of the year (end of the 31st of december)
-
1
def end_of_year
-
change(:month => 12, :day => 31, :hour => 23, :min => 59, :sec => 59, :usec => 999999.999)
-
end
-
1
alias :at_end_of_year :end_of_year
-
-
# Convenience method which returns a new Time representing the time 1 day ago
-
1
def yesterday
-
advance(:days => -1)
-
end
-
-
# Convenience method which returns a new Time representing the time 1 day since the instance time
-
1
def tomorrow
-
advance(:days => 1)
-
end
-
-
1
def plus_with_duration(other) #:nodoc:
-
if ActiveSupport::Duration === other
-
other.since(self)
-
else
-
plus_without_duration(other)
-
end
-
end
-
1
alias_method :plus_without_duration, :+
-
1
alias_method :+, :plus_with_duration
-
-
1
def minus_with_duration(other) #:nodoc:
-
9
if ActiveSupport::Duration === other
-
other.until(self)
-
else
-
9
minus_without_duration(other)
-
end
-
end
-
1
alias_method :minus_without_duration, :-
-
1
alias_method :-, :minus_with_duration
-
-
# Time#- can also be used to determine the number of seconds between two Time instances.
-
# We're layering on additional behavior so that ActiveSupport::TimeWithZone instances
-
# are coerced into values that Time#- will recognize
-
1
def minus_with_coercion(other)
-
9
other = other.comparable_time if other.respond_to?(:comparable_time)
-
9
other.is_a?(DateTime) ? to_f - other.to_f : minus_without_coercion(other)
-
end
-
1
alias_method :minus_without_coercion, :-
-
1
alias_method :-, :minus_with_coercion
-
-
# Layers additional behavior on Time#<=> so that DateTime and ActiveSupport::TimeWithZone instances
-
# can be chronologically compared with a Time
-
1
def compare_with_coercion(other)
-
# we're avoiding Time#to_datetime cause it's expensive
-
5
other.is_a?(Time) ? compare_without_coercion(other.to_time) : to_datetime <=> other
-
end
-
1
alias_method :compare_without_coercion, :<=>
-
1
alias_method :<=>, :compare_with_coercion
-
end
-
1
require 'active_support/inflector/methods'
-
1
require 'active_support/core_ext/time/publicize_conversion_methods'
-
1
require 'active_support/values/time_zone'
-
-
1
class Time
-
1
DATE_FORMATS = {
-
:db => "%Y-%m-%d %H:%M:%S",
-
:number => "%Y%m%d%H%M%S",
-
:time => "%H:%M",
-
:short => "%d %b %H:%M",
-
:long => "%B %d, %Y %H:%M",
-
:long_ordinal => lambda { |time| time.strftime("%B #{ActiveSupport::Inflector.ordinalize(time.day)}, %Y %H:%M") },
-
:rfc822 => lambda { |time| time.strftime("%a, %d %b %Y %H:%M:%S #{time.formatted_offset(false)}") }
-
}
-
-
# Converts to a formatted string. See DATE_FORMATS for builtin formats.
-
#
-
# This method is aliased to <tt>to_s</tt>.
-
#
-
# time = Time.now # => Thu Jan 18 06:10:17 CST 2007
-
#
-
# time.to_formatted_s(:time) # => "06:10"
-
# time.to_s(:time) # => "06:10"
-
#
-
# time.to_formatted_s(:db) # => "2007-01-18 06:10:17"
-
# time.to_formatted_s(:number) # => "20070118061017"
-
# time.to_formatted_s(:short) # => "18 Jan 06:10"
-
# time.to_formatted_s(:long) # => "January 18, 2007 06:10"
-
# time.to_formatted_s(:long_ordinal) # => "January 18th, 2007 06:10"
-
# time.to_formatted_s(:rfc822) # => "Thu, 18 Jan 2007 06:10:17 -0600"
-
#
-
# == Adding your own time formats to +to_formatted_s+
-
# You can add your own formats to the Time::DATE_FORMATS hash.
-
# Use the format name as the hash key and either a strftime string
-
# or Proc instance that takes a time argument as the value.
-
#
-
# # config/initializers/time_formats.rb
-
# Time::DATE_FORMATS[:month_and_year] = "%B %Y"
-
# Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") }
-
1
def to_formatted_s(format = :default)
-
if formatter = DATE_FORMATS[format]
-
formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
-
else
-
to_default_s
-
end
-
end
-
1
alias_method :to_default_s, :to_s
-
1
alias_method :to_s, :to_formatted_s
-
-
# Returns the UTC offset as an +HH:MM formatted string.
-
#
-
# Time.local(2000).formatted_offset # => "-06:00"
-
# Time.local(2000).formatted_offset(false) # => "-0600"
-
1
def formatted_offset(colon = true, alternate_utc_string = nil)
-
utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon)
-
end
-
-
# A method to keep Time, Date and DateTime instances interchangeable on conversions.
-
# In this case, it simply returns +self+.
-
def to_time
-
self
-
1
end unless method_defined?(:to_time)
-
end
-
# Pre-1.9 versions of Ruby have a bug with marshaling Time instances, where utc instances are
-
# unmarshalled in the local zone, instead of utc. We're layering behavior on the _dump and _load
-
# methods so that utc instances can be flagged on dump, and coerced back to utc on load.
-
1
if !Marshal.load(Marshal.dump(Time.now.utc)).utc?
-
class Time
-
class << self
-
alias_method :_load_without_utc_flag, :_load
-
def _load(marshaled_time)
-
time = _load_without_utc_flag(marshaled_time)
-
time.instance_eval do
-
if defined?(@marshal_with_utc_coercion)
-
val = remove_instance_variable("@marshal_with_utc_coercion")
-
end
-
val ? utc : self
-
end
-
end
-
end
-
-
alias_method :_dump_without_utc_flag, :_dump
-
def _dump(*args)
-
obj = dup
-
obj.instance_variable_set('@marshal_with_utc_coercion', utc?)
-
obj._dump_without_utc_flag(*args)
-
end
-
end
-
end
-
-
# Ruby 1.9.2 adds utc_offset and zone to Time, but marshaling only
-
# preserves utc_offset. Preserve zone also, even though it may not
-
# work in some edge cases.
-
1
if Time.local(2010).zone != Marshal.load(Marshal.dump(Time.local(2010))).zone
-
1
class Time
-
1
class << self
-
1
alias_method :_load_without_zone, :_load
-
1
def _load(marshaled_time)
-
time = _load_without_zone(marshaled_time)
-
time.instance_eval do
-
if zone = defined?(@_zone) && remove_instance_variable('@_zone')
-
ary = to_a
-
ary[0] += subsec if ary[0] == sec
-
ary[-1] = zone
-
utc? ? Time.utc(*ary) : Time.local(*ary)
-
else
-
self
-
end
-
end
-
end
-
end
-
-
1
alias_method :_dump_without_zone, :_dump
-
1
def _dump(*args)
-
obj = dup
-
obj.instance_variable_set('@_zone', zone)
-
obj._dump_without_zone(*args)
-
end
-
end
-
end
-
1
require 'date'
-
-
1
class Time
-
# Ruby 1.8-cvs and early 1.9 series define private Time#to_date
-
1
%w(to_date to_datetime).each do |method|
-
2
if private_instance_methods.include?(method) || private_instance_methods.include?(method.to_sym)
-
public method
-
end
-
end
-
end
-
1
require 'active_support/time_with_zone'
-
-
1
class Time
-
1
class << self
-
1
attr_accessor :zone_default
-
-
# Returns the TimeZone for the current request, if this has been set (via Time.zone=).
-
# If <tt>Time.zone</tt> has not been set for the current request, returns the TimeZone specified in <tt>config.time_zone</tt>.
-
1
def zone
-
Thread.current[:time_zone] || zone_default
-
end
-
-
# Sets <tt>Time.zone</tt> to a TimeZone object for the current request/thread.
-
#
-
# This method accepts any of the following:
-
#
-
# * A Rails TimeZone object.
-
# * An identifier for a Rails TimeZone object (e.g., "Eastern Time (US & Canada)", <tt>-5.hours</tt>).
-
# * A TZInfo::Timezone object.
-
# * An identifier for a TZInfo::Timezone object (e.g., "America/New_York").
-
#
-
# Here's an example of how you might set <tt>Time.zone</tt> on a per request basis and reset it when the request is done.
-
# <tt>current_user.time_zone</tt> just needs to return a string identifying the user's preferred time zone:
-
#
-
# class ApplicationController < ActionController::Base
-
# around_filter :set_time_zone
-
#
-
# def set_time_zone
-
# old_time_zone = Time.zone
-
# Time.zone = current_user.time_zone if logged_in?
-
# yield
-
# ensure
-
# Time.zone = old_time_zone
-
# end
-
# end
-
1
def zone=(time_zone)
-
Thread.current[:time_zone] = find_zone!(time_zone)
-
end
-
-
# Allows override of <tt>Time.zone</tt> locally inside supplied block; resets <tt>Time.zone</tt> to existing value when done.
-
1
def use_zone(time_zone)
-
new_zone = find_zone!(time_zone)
-
begin
-
old_zone, ::Time.zone = ::Time.zone, new_zone
-
yield
-
ensure
-
::Time.zone = old_zone
-
end
-
end
-
-
# Returns a TimeZone instance or nil, or raises an ArgumentError for invalid timezones.
-
1
def find_zone!(time_zone)
-
1
return time_zone if time_zone.nil? || time_zone.is_a?(ActiveSupport::TimeZone)
-
# lookup timezone based on identifier (unless we've been passed a TZInfo::Timezone)
-
1
unless time_zone.respond_to?(:period_for_local)
-
1
time_zone = ActiveSupport::TimeZone[time_zone] || TZInfo::Timezone.get(time_zone)
-
end
-
# Return if a TimeZone instance, or wrap in a TimeZone instance if a TZInfo::Timezone
-
1
time_zone.is_a?(ActiveSupport::TimeZone) ? time_zone : ActiveSupport::TimeZone.create(time_zone.name, nil, time_zone)
-
rescue TZInfo::InvalidTimezoneIdentifier
-
raise ArgumentError, "Invalid Timezone: #{time_zone}"
-
end
-
-
1
def find_zone(time_zone)
-
find_zone!(time_zone) rescue nil
-
end
-
end
-
-
# Returns the simultaneous time in <tt>Time.zone</tt>.
-
#
-
# Time.zone = 'Hawaii' # => 'Hawaii'
-
# Time.utc(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00
-
#
-
# This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt> as the local zone
-
# instead of the operating system's time zone.
-
#
-
# You can also pass in a TimeZone instance or string that identifies a TimeZone as an argument,
-
# and the conversion will be based on that zone instead of <tt>Time.zone</tt>.
-
#
-
# Time.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
-
1
def in_time_zone(zone = ::Time.zone)
-
return self unless zone
-
-
ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.find_zone!(zone))
-
end
-
end
-
# encoding: utf-8
-
-
1
if RUBY_VERSION >= '1.9'
-
1
require 'uri'
-
-
1
str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese.
-
-
1
parser = URI::Parser.new
-
-
1
unless str == parser.unescape(parser.escape(str))
-
1
URI::Parser.class_eval do
-
1
remove_method :unescape
-
1
def unescape(str, escaped = /%[a-fA-F\d]{2}/)
-
# TODO: Are we actually sure that ASCII == UTF-8?
-
# YK: My initial experiments say yes, but let's be sure please
-
enc = str.encoding
-
enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
-
str.gsub(escaped) { [$&[1, 2].hex].pack('C') }.force_encoding(enc)
-
end
-
end
-
end
-
end
-
-
1
module URI
-
1
class << self
-
1
def parser
-
@parser ||= URI.const_defined?(:Parser) ? URI::Parser.new : URI
-
end
-
end
-
end
-
1
require 'set'
-
1
require 'thread'
-
1
require 'pathname'
-
1
require 'active_support/core_ext/module/aliasing'
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'active_support/core_ext/module/introspection'
-
1
require 'active_support/core_ext/module/anonymous'
-
1
require 'active_support/core_ext/module/deprecation'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/load_error'
-
1
require 'active_support/core_ext/name_error'
-
1
require 'active_support/core_ext/string/starts_ends_with'
-
1
require 'active_support/inflector'
-
-
1
module ActiveSupport #:nodoc:
-
1
module Dependencies #:nodoc:
-
1
extend self
-
-
# Should we turn on Ruby warnings on the first load of dependent files?
-
1
mattr_accessor :warnings_on_first_load
-
1
self.warnings_on_first_load = false
-
-
# All files ever loaded.
-
1
mattr_accessor :history
-
1
self.history = Set.new
-
-
# All files currently loaded.
-
1
mattr_accessor :loaded
-
1
self.loaded = Set.new
-
-
# Should we load files or require them?
-
1
mattr_accessor :mechanism
-
1
self.mechanism = ENV['NO_RELOAD'] ? :require : :load
-
-
# The set of directories from which we may automatically load files. Files
-
# under these directories will be reloaded on each request in development mode,
-
# unless the directory also appears in autoload_once_paths.
-
1
mattr_accessor :autoload_paths
-
1
self.autoload_paths = []
-
-
# The set of directories from which automatically loaded constants are loaded
-
# only once. All directories in this set must also be present in +autoload_paths+.
-
1
mattr_accessor :autoload_once_paths
-
1
self.autoload_once_paths = []
-
-
# An array of qualified constant names that have been loaded. Adding a name to
-
# this array will cause it to be unloaded the next time Dependencies are cleared.
-
1
mattr_accessor :autoloaded_constants
-
1
self.autoloaded_constants = []
-
-
# An array of constant names that need to be unloaded on every request. Used
-
# to allow arbitrary constants to be marked for unloading.
-
1
mattr_accessor :explicitly_unloadable_constants
-
1
self.explicitly_unloadable_constants = []
-
-
# The logger is used for generating information on the action run-time (including benchmarking) if available.
-
# Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
-
1
mattr_accessor :logger
-
-
# Set to true to enable logging of const_missing and file loads
-
1
mattr_accessor :log_activity
-
1
self.log_activity = false
-
-
# The WatchStack keeps a stack of the modules being watched as files are loaded.
-
# If a file in the process of being loaded (parent.rb) triggers the load of
-
# another file (child.rb) the stack will ensure that child.rb handles the new
-
# constants.
-
#
-
# If child.rb is being autoloaded, its constants will be added to
-
# autoloaded_constants. If it was being `require`d, they will be discarded.
-
#
-
# This is handled by walking back up the watch stack and adding the constants
-
# found by child.rb to the list of original constants in parent.rb
-
1
class WatchStack < Hash
-
# @watching is a stack of lists of constants being watched. For instance,
-
# if parent.rb is autoloaded, the stack will look like [[Object]]. If parent.rb
-
# then requires namespace/child.rb, the stack will look like [[Object], [Namespace]].
-
-
1
def initialize
-
1
@watching = []
-
2
super { |h,k| h[k] = [] }
-
end
-
-
# return a list of new constants found since the last call to watch_namespaces
-
1
def new_constants
-
243
constants = []
-
-
# Grab the list of namespaces that we're looking for new constants under
-
243
@watching.last.each do |namespace|
-
# Retrieve the constants that were present under the namespace when watch_namespaces
-
# was originally called
-
243
original_constants = self[namespace].last
-
-
243
mod = Inflector.constantize(namespace) if Dependencies.qualified_const_defined?(namespace)
-
243
next unless mod.is_a?(Module)
-
-
# Get a list of the constants that were added
-
243
new_constants = mod.local_constant_names - original_constants
-
-
# self[namespace] returns an Array of the constants that are being evaluated
-
# for that namespace. For instance, if parent.rb requires child.rb, the first
-
# element of self[Object] will be an Array of the constants that were present
-
# before parent.rb was required. The second element will be an Array of the
-
# constants that were present before child.rb was required.
-
243
self[namespace].each do |namespace_constants|
-
744
namespace_constants.concat(new_constants)
-
end
-
-
# Normalize the list of new constants, and add them to the list we will return
-
243
new_constants.each do |suffix|
-
2
constants << ([namespace, suffix] - ["Object"]).join("::")
-
end
-
end
-
243
constants
-
ensure
-
# A call to new_constants is always called after a call to watch_namespaces
-
243
pop_modules(@watching.pop)
-
end
-
-
# Add a set of modules to the watch stack, remembering the initial constants
-
1
def watch_namespaces(namespaces)
-
243
watching = []
-
243
namespaces.map do |namespace|
-
243
module_name = Dependencies.to_constant_name(namespace)
-
243
original_constants = Dependencies.qualified_const_defined?(module_name) ?
-
Inflector.constantize(module_name).local_constant_names : []
-
-
243
watching << module_name
-
243
self[module_name] << original_constants
-
end
-
243
@watching << watching
-
end
-
-
1
def pop_modules(modules)
-
486
modules.each { |mod| self[mod].pop }
-
end
-
end
-
-
# An internal stack used to record which constants are loaded by any block.
-
1
mattr_accessor :constant_watch_stack
-
1
self.constant_watch_stack = WatchStack.new
-
-
# Module includes this module
-
1
module ModuleConstMissing #:nodoc:
-
1
def self.append_features(base)
-
1
base.class_eval do
-
# Emulate #exclude via an ivar
-
1
return if defined?(@_const_missing) && @_const_missing
-
1
@_const_missing = instance_method(:const_missing)
-
1
remove_method(:const_missing)
-
end
-
1
super
-
end
-
-
1
def self.exclude_from(base)
-
base.class_eval do
-
define_method :const_missing, @_const_missing
-
@_const_missing = nil
-
end
-
end
-
-
# Use const_missing to autoload associations so we don't have to
-
# require_association when using single-table inheritance.
-
1
def const_missing(const_name, nesting = nil)
-
10
klass_name = name.presence || "Object"
-
-
10
unless nesting
-
# We'll assume that the nesting of Foo::Bar is ["Foo::Bar", "Foo"]
-
# even though it might not be, such as in the case of
-
# class Foo::Bar; Baz; end
-
10
nesting = []
-
21
klass_name.to_s.scan(/::|$/) { nesting.unshift $` }
-
end
-
-
# If there are multiple levels of nesting to search under, the top
-
# level is the one we want to report as the lookup fail.
-
10
error = nil
-
-
10
nesting.each do |namespace|
-
11
begin
-
11
return Dependencies.load_missing_constant Inflector.constantize(namespace), const_name
-
rescue NoMethodError then raise
-
rescue NameError => e
-
7
error ||= e
-
end
-
end
-
-
# Raise the first error for this set. If this const_missing came from an
-
# earlier const_missing, this will result in the real error bubbling
-
# all the way up
-
6
raise error
-
end
-
-
1
def unloadable(const_desc = self)
-
super(const_desc)
-
end
-
end
-
-
# Object includes this module
-
1
module Loadable #:nodoc:
-
1
def self.exclude_from(base)
-
base.class_eval { define_method(:load, Kernel.instance_method(:load)) }
-
end
-
-
1
def require_or_load(file_name)
-
Dependencies.require_or_load(file_name)
-
end
-
-
1
def require_dependency(file_name, message = "No such file to load -- %s")
-
26
unless file_name.is_a?(String)
-
raise ArgumentError, "the file name must be a String -- you passed #{file_name.inspect}"
-
end
-
-
26
Dependencies.depend_on(file_name, false, message)
-
end
-
-
1
def require_association(file_name)
-
Dependencies.associate_with(file_name)
-
end
-
-
1
def load_dependency(file)
-
988
if Dependencies.load?
-
486
Dependencies.new_constants_in(Object) { yield }.presence
-
else
-
745
yield
-
end
-
rescue Exception => exception # errors from loading file
-
10
exception.blame_file! file
-
10
raise
-
end
-
-
1
def load(file, *)
-
16
result = false
-
32
load_dependency(file) { result = super }
-
16
result
-
end
-
-
1
def require(file, *)
-
972
result = false
-
1944
load_dependency(file) { result = super }
-
962
result
-
end
-
-
# Mark the given constant as unloadable. Unloadable constants are removed each
-
# time dependencies are cleared.
-
#
-
# Note that marking a constant for unloading need only be done once. Setup
-
# or init scripts may list each unloadable constant that may need unloading;
-
# each constant will be removed for every subsequent clear, as opposed to for
-
# the first clear.
-
#
-
# The provided constant descriptor may be a (non-anonymous) module or class,
-
# or a qualified constant name as a string or symbol.
-
#
-
# Returns true if the constant was not previously marked for unloading, false
-
# otherwise.
-
1
def unloadable(const_desc)
-
Dependencies.mark_for_unload const_desc
-
end
-
end
-
-
# Exception file-blaming
-
1
module Blamable #:nodoc:
-
1
def blame_file!(file)
-
10
(@blamed_files ||= []).unshift file
-
end
-
-
1
def blamed_files
-
@blamed_files ||= []
-
end
-
-
1
def describe_blame
-
return nil if blamed_files.empty?
-
"This error occurred while loading the following files:\n #{blamed_files.join "\n "}"
-
end
-
-
1
def copy_blame!(exc)
-
@blamed_files = exc.blamed_files.clone
-
self
-
end
-
end
-
-
1
def hook!
-
2
Object.class_eval { include Loadable }
-
2
Module.class_eval { include ModuleConstMissing }
-
2
Exception.class_eval { include Blamable }
-
1
true
-
end
-
-
1
def unhook!
-
ModuleConstMissing.exclude_from(Module)
-
Loadable.exclude_from(Object)
-
true
-
end
-
-
1
def load?
-
1006
mechanism == :load
-
end
-
-
1
def depend_on(file_name, swallow_load_errors = false, message = "No such file to load -- %s.rb")
-
26
path = search_for_file(file_name)
-
26
require_or_load(path || file_name)
-
rescue LoadError => load_error
-
unless swallow_load_errors
-
if file_name = load_error.message[/ -- (.*?)(\.rb)?$/, 1]
-
raise LoadError.new(message % file_name).copy_blame!(load_error)
-
end
-
raise
-
end
-
end
-
-
1
def associate_with(file_name)
-
depend_on(file_name, true)
-
end
-
-
1
def clear
-
log_call
-
loaded.clear
-
remove_unloadable_constants!
-
end
-
-
1
def require_or_load(file_name, const_path = nil)
-
29
log_call file_name, const_path
-
29
file_name = $1 if file_name =~ /^(.*)\.rb$/
-
29
expanded = File.expand_path(file_name)
-
29
return if loaded.include?(expanded)
-
-
# Record that we've seen this file *before* loading it to avoid an
-
# infinite loop with mutual dependencies.
-
18
loaded << expanded
-
-
18
begin
-
18
if load?
-
log "loading #{file_name}"
-
-
# Enable warnings if this file has not been loaded before and
-
# warnings_on_first_load is set.
-
load_args = ["#{file_name}.rb"]
-
load_args << const_path unless const_path.nil?
-
-
if !warnings_on_first_load or history.include?(expanded)
-
result = load_file(*load_args)
-
else
-
enable_warnings { result = load_file(*load_args) }
-
end
-
else
-
18
log "requiring #{file_name}"
-
18
result = require file_name
-
end
-
rescue Exception
-
loaded.delete expanded
-
raise
-
end
-
-
# Record history *after* loading so first load gets warnings.
-
18
history << expanded
-
18
return result
-
end
-
-
# Is the provided constant path defined?
-
1
def qualified_const_defined?(path)
-
497
names = path.sub(/^::/, '').to_s.split('::')
-
-
497
names.inject(Object) do |mod, name|
-
498
return false unless local_const_defined?(mod, name)
-
498
mod.const_get name
-
end
-
end
-
-
1
if Module.method(:const_defined?).arity == 1
-
# Does this module define this constant?
-
# Wrapper to accommodate changing Module#const_defined? in Ruby 1.9
-
def local_const_defined?(mod, const)
-
mod.const_defined?(const)
-
end
-
else
-
1
def local_const_defined?(mod, const) #:nodoc:
-
517
mod.const_defined?(const, false)
-
end
-
end
-
-
# Given +path+, a filesystem path to a ruby file, return an array of constant
-
# paths which would cause Dependencies to attempt to load this file.
-
1
def loadable_constants_for_path(path, bases = autoload_paths)
-
path = $1 if path =~ /\A(.*)\.rb\Z/
-
expanded_path = File.expand_path(path)
-
paths = []
-
-
bases.each do |root|
-
expanded_root = File.expand_path(root)
-
next unless %r{\A#{Regexp.escape(expanded_root)}(/|\\)} =~ expanded_path
-
-
nesting = expanded_path[(expanded_root.size)..-1]
-
nesting = nesting[1..-1] if nesting && nesting[0] == ?/
-
next if nesting.blank?
-
-
paths << nesting.camelize
-
end
-
-
paths.uniq!
-
paths
-
end
-
-
# Search for a file in autoload_paths matching the provided suffix.
-
1
def search_for_file(path_suffix)
-
37
path_suffix = path_suffix.sub(/(\.rb)?$/, ".rb")
-
-
37
autoload_paths.each do |root|
-
149
path = File.join(root, path_suffix)
-
149
return path if File.file? path
-
end
-
nil # Gee, I sure wish we had first_match ;-)
-
end
-
-
# Does the provided path_suffix correspond to an autoloadable module?
-
# Instead of returning a boolean, the autoload base for this module is returned.
-
1
def autoloadable_module?(path_suffix)
-
8
autoload_paths.each do |load_path|
-
40
return load_path if File.directory? File.join(load_path, path_suffix)
-
end
-
nil
-
end
-
-
1
def load_once_path?(path)
-
autoload_once_paths.any? { |base| path.starts_with? base }
-
end
-
-
# Attempt to autoload the provided module name by searching for a directory
-
# matching the expect path suffix. If found, the module is created and assigned
-
# to +into+'s constants with the name +const_name+. Provided that the directory
-
# was loaded from a reloadable base path, it is added to the set of constants
-
# that are to be unloaded.
-
1
def autoload_module!(into, const_name, qualified_name, path_suffix)
-
8
return nil unless base_path = autoloadable_module?(path_suffix)
-
mod = Module.new
-
into.const_set const_name, mod
-
autoloaded_constants << qualified_name unless autoload_once_paths.include?(base_path)
-
return mod
-
end
-
-
# Load the file at the provided path. +const_paths+ is a set of qualified
-
# constant names. When loading the file, Dependencies will watch for the
-
# addition of these constants. Each that is defined will be marked as
-
# autoloaded, and will be removed when Dependencies.clear is next called.
-
#
-
# If the second parameter is left off, then Dependencies will construct a set
-
# of names that the file at +path+ may define. See
-
# +loadable_constants_for_path+ for more details.
-
1
def load_file(path, const_paths = loadable_constants_for_path(path))
-
log_call path, const_paths
-
const_paths = [const_paths].compact unless const_paths.is_a? Array
-
parent_paths = const_paths.collect { |const_path| /(.*)::[^:]+\Z/ =~ const_path ? $1 : :Object }
-
-
result = nil
-
newly_defined_paths = new_constants_in(*parent_paths) do
-
result = Kernel.load path
-
end
-
-
autoloaded_constants.concat newly_defined_paths unless load_once_path?(path)
-
autoloaded_constants.uniq!
-
log "loading #{path} defined #{newly_defined_paths * ', '}" unless newly_defined_paths.empty?
-
return result
-
end
-
-
# Return the constant path for the provided parent and constant name.
-
1
def qualified_name_for(mod, name)
-
14
mod_name = to_constant_name mod
-
14
mod_name == "Object" ? name.to_s : "#{mod_name}::#{name}"
-
end
-
-
# Load the constant named +const_name+ which is missing from +from_mod+. If
-
# it is not possible to load the constant into from_mod, try its parent module
-
# using const_missing.
-
1
def load_missing_constant(from_mod, const_name)
-
11
log_call from_mod, const_name
-
-
11
unless qualified_const_defined?(from_mod.name) && Inflector.constantize(from_mod.name).equal?(from_mod)
-
raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
-
end
-
-
11
raise NameError, "#{from_mod} is not missing constant #{const_name}!" if local_const_defined?(from_mod, const_name)
-
-
11
qualified_name = qualified_name_for from_mod, const_name
-
11
path_suffix = qualified_name.underscore
-
-
11
file_path = search_for_file(path_suffix)
-
-
11
if file_path && ! loaded.include?(File.expand_path(file_path)) # We found a matching file to load
-
3
require_or_load file_path
-
3
raise LoadError, "Expected #{file_path} to define #{qualified_name}" unless local_const_defined?(from_mod, const_name)
-
3
return from_mod.const_get(const_name)
-
elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix)
-
return mod
-
8
elsif (parent = from_mod.parent) && parent != from_mod &&
-
5
! from_mod.parents.any? { |p| local_const_defined?(p, const_name) }
-
# If our parents do not have a constant named +const_name+ then we are free
-
# to attempt to load upwards. If they do have such a constant, then this
-
# const_missing must be due to from_mod::const_name, which should not
-
# return constants from from_mod's parents.
-
4
begin
-
4
return parent.const_missing(const_name)
-
rescue NameError => e
-
3
raise unless e.missing_name? qualified_name_for(parent, const_name)
-
end
-
end
-
-
7
raise NameError,
-
"uninitialized constant #{qualified_name}",
-
211
caller.reject {|l| l.starts_with? __FILE__ }
-
end
-
-
# Remove the constants that have been autoloaded, and those that have been
-
# marked for unloading. Before each constant is removed a callback is sent
-
# to its class/module if it implements +before_remove_const+.
-
#
-
# The callback implementation should be restricted to cleaning up caches, etc.
-
# as the environment will be in an inconsistent state, e.g. other constants
-
# may have already been unloaded and not accessible.
-
1
def remove_unloadable_constants!
-
autoloaded_constants.each { |const| remove_constant const }
-
autoloaded_constants.clear
-
Reference.clear!
-
explicitly_unloadable_constants.each { |const| remove_constant const }
-
end
-
-
1
class ClassCache
-
1
def initialize
-
3
@store = Hash.new { |h, k| h[k] = Inflector.constantize(k) }
-
end
-
-
1
def empty?
-
@store.empty?
-
end
-
-
1
def key?(key)
-
@store.key?(key)
-
end
-
-
1
def []=(key, value)
-
return unless key.respond_to?(:name)
-
-
raise(ArgumentError, 'anonymous classes cannot be cached') if key.name.blank?
-
-
@store[key.name] = value
-
end
-
-
1
def [](key)
-
2
key = key.name if key.respond_to?(:name)
-
-
2
@store[key]
-
end
-
1
alias :get :[]
-
-
1
class Getter # :nodoc:
-
1
def initialize(name)
-
@name = name
-
end
-
-
1
def get
-
Reference.get @name
-
end
-
1
deprecate :get
-
end
-
-
1
def new(name)
-
self[name] = name
-
Getter.new(name)
-
end
-
1
deprecate :new
-
-
1
def store(name)
-
self[name] = name
-
self
-
end
-
-
1
def clear!
-
@store.clear
-
end
-
end
-
-
1
Reference = ClassCache.new
-
-
1
def ref(name)
-
Reference.new(name)
-
end
-
1
deprecate :ref
-
-
# Store a reference to a class +klass+.
-
1
def reference(klass)
-
Reference.store klass
-
end
-
-
# Get the reference for class named +name+.
-
1
def constantize(name)
-
Reference.get(name)
-
end
-
-
# Determine if the given constant has been automatically loaded.
-
1
def autoloaded?(desc)
-
# No name => anonymous module.
-
return false if desc.is_a?(Module) && desc.anonymous?
-
name = to_constant_name desc
-
return false unless qualified_const_defined? name
-
return autoloaded_constants.include?(name)
-
end
-
-
# Will the provided constant descriptor be unloaded?
-
1
def will_unload?(const_desc)
-
autoloaded?(const_desc) ||
-
explicitly_unloadable_constants.include?(to_constant_name(const_desc))
-
end
-
-
# Mark the provided constant name for unloading. This constant will be
-
# unloaded on each request, not just the next one.
-
1
def mark_for_unload(const_desc)
-
name = to_constant_name const_desc
-
if explicitly_unloadable_constants.include? name
-
return false
-
else
-
explicitly_unloadable_constants << name
-
return true
-
end
-
end
-
-
# Run the provided block and detect the new constants that were loaded during
-
# its execution. Constants may only be regarded as 'new' once -- so if the
-
# block calls +new_constants_in+ again, then the constants defined within the
-
# inner call will not be reported in this one.
-
#
-
# If the provided block does not run to completion, and instead raises an
-
# exception, any new constants are regarded as being only partially defined
-
# and will be removed immediately.
-
1
def new_constants_in(*descs)
-
243
log_call(*descs)
-
-
243
constant_watch_stack.watch_namespaces(descs)
-
243
aborting = true
-
-
243
begin
-
243
yield # Now yield to the code that is to define new constants.
-
240
aborting = false
-
ensure
-
243
new_constants = constant_watch_stack.new_constants
-
-
243
log "New constants: #{new_constants * ', '}"
-
243
return new_constants unless aborting
-
-
3
log "Error during loading, removing partially loaded constants "
-
3
new_constants.each {|c| remove_constant(c) }.clear
-
end
-
-
return []
-
end
-
-
# Convert the provided const desc to a qualified constant name (as a string).
-
# A module, class, symbol, or string may be provided.
-
1
def to_constant_name(desc) #:nodoc:
-
257
case desc
-
when String then desc.sub(/^::/, '')
-
when Symbol then desc.to_s
-
when Module
-
desc.name.presence ||
-
257
raise(ArgumentError, "Anonymous modules have no name to be referenced by")
-
else raise TypeError, "Not a valid constant descriptor: #{desc.inspect}"
-
end
-
end
-
-
1
def remove_constant(const) #:nodoc:
-
return false unless qualified_const_defined? const
-
-
# Normalize ::Foo, Foo, Object::Foo, and ::Object::Foo to Object::Foo
-
names = const.to_s.sub(/^::(Object)?/, 'Object::').split("::")
-
to_remove = names.pop
-
parent = Inflector.constantize(names * '::')
-
-
log "removing constant #{const}"
-
constantized = constantize(const)
-
constantized.before_remove_const if constantized.respond_to?(:before_remove_const)
-
parent.instance_eval { remove_const to_remove }
-
-
return true
-
end
-
-
1
protected
-
1
def log_call(*args)
-
283
if log_activity?
-
arg_str = args.collect { |arg| arg.inspect } * ', '
-
/in `([a-z_\?\!]+)'/ =~ caller(1).first
-
selector = $1 || '<unknown>'
-
log "called #{selector}(#{arg_str})"
-
end
-
end
-
-
1
def log(msg)
-
264
logger.debug "Dependencies: #{msg}" if log_activity?
-
end
-
-
1
def log_activity?
-
547
logger && log_activity
-
end
-
end
-
end
-
-
1
ActiveSupport::Dependencies.hook!
-
1
module ActiveSupport
-
# This module provides an internal implementation to track descendants
-
# which is faster than iterating through ObjectSpace.
-
1
module DescendantsTracker
-
18
@@direct_descendants = Hash.new { |h, k| h[k] = [] }
-
-
1
def self.direct_descendants(klass)
-
23
@@direct_descendants[klass]
-
end
-
-
1
def self.descendants(klass)
-
47
@@direct_descendants[klass].inject([]) do |descendants, _klass|
-
9
descendants << _klass
-
9
descendants.concat _klass.descendants
-
end
-
end
-
-
1
def self.clear
-
if defined? ActiveSupport::Dependencies
-
@@direct_descendants.each do |klass, descendants|
-
if ActiveSupport::Dependencies.autoloaded?(klass)
-
@@direct_descendants.delete(klass)
-
else
-
descendants.reject! { |v| ActiveSupport::Dependencies.autoloaded?(v) }
-
end
-
end
-
else
-
@@direct_descendants.clear
-
end
-
end
-
-
1
def inherited(base)
-
23
self.direct_descendants << base
-
23
super
-
end
-
-
1
def direct_descendants
-
23
DescendantsTracker.direct_descendants(self)
-
end
-
-
1
def descendants
-
9
DescendantsTracker.descendants(self)
-
end
-
end
-
end
-
1
require 'active_support/basic_object'
-
1
require 'active_support/core_ext/array/conversions'
-
1
require 'active_support/core_ext/object/acts_like'
-
-
1
module ActiveSupport
-
# Provides accurate date and time measurements using Date#advance and
-
# Time#advance, respectively. It mainly supports the methods on Numeric.
-
# Example:
-
#
-
# 1.month.ago # equivalent to Time.now.advance(:months => -1)
-
1
class Duration < BasicObject
-
1
attr_accessor :value, :parts
-
1
delegate :duplicable?, :to => :value # required when using ActiveSupport's BasicObject on 1.8
-
-
1
def initialize(value, parts) #:nodoc:
-
@value, @parts = value, parts
-
end
-
-
# Adds another Duration or a Numeric to this Duration. Numeric values
-
# are treated as seconds.
-
1
def +(other)
-
if Duration === other
-
Duration.new(value + other.value, @parts + other.parts)
-
else
-
Duration.new(value + other, @parts + [[:seconds, other]])
-
end
-
end
-
-
# Subtracts another Duration or a Numeric from this Duration. Numeric
-
# values are treated as seconds.
-
1
def -(other)
-
self + (-other)
-
end
-
-
1
def -@ #:nodoc:
-
Duration.new(-value, parts.map { |type,number| [type, -number] })
-
end
-
-
1
def is_a?(klass) #:nodoc:
-
Duration == klass || value.is_a?(klass)
-
end
-
1
alias :kind_of? :is_a?
-
-
# Returns true if <tt>other</tt> is also a Duration instance with the
-
# same <tt>value</tt>, or if <tt>other == value</tt>.
-
1
def ==(other)
-
if Duration === other
-
other.value == value
-
else
-
other == value
-
end
-
end
-
-
1
def self.===(other) #:nodoc:
-
9
other.is_a?(Duration)
-
rescue ::NoMethodError
-
false
-
end
-
-
# Calculates a new Time or Date that is as far in the future
-
# as this Duration represents.
-
1
def since(time = ::Time.current)
-
sum(1, time)
-
end
-
1
alias :from_now :since
-
-
# Calculates a new Time or Date that is as far in the past
-
# as this Duration represents.
-
1
def ago(time = ::Time.current)
-
sum(-1, time)
-
end
-
1
alias :until :ago
-
-
1
def inspect #:nodoc:
-
consolidated = parts.inject(::Hash.new(0)) { |h,part| h[part.first] += part.last; h }
-
parts = [:years, :months, :days, :minutes, :seconds].map do |length|
-
n = consolidated[length]
-
"#{n} #{n == 1 ? length.to_s.singularize : length.to_s}" if n.nonzero?
-
end.compact
-
parts = ["0 seconds"] if parts.empty?
-
parts.to_sentence(:locale => :en)
-
end
-
-
1
def as_json(options = nil) #:nodoc:
-
to_i
-
end
-
-
1
protected
-
-
1
def sum(sign, time = ::Time.current) #:nodoc:
-
parts.inject(time) do |t,(type,number)|
-
if t.acts_like?(:time) || t.acts_like?(:date)
-
if type == :seconds
-
t.since(sign * number)
-
else
-
t.advance(type => sign * number)
-
end
-
else
-
raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
-
end
-
end
-
end
-
-
1
private
-
-
1
def method_missing(method, *args, &block) #:nodoc:
-
value.send(method, *args, &block)
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/keys'
-
-
# This class has dubious semantics and we only have it so that
-
# people can write <tt>params[:key]</tt> instead of <tt>params['key']</tt>
-
# and they get the same value for both keys.
-
-
1
module ActiveSupport
-
1
class HashWithIndifferentAccess < Hash
-
-
# Always returns true, so that <tt>Array#extract_options!</tt> finds members of this class.
-
1
def extractable_options?
-
true
-
end
-
-
1
def with_indifferent_access
-
dup
-
end
-
-
1
def initialize(constructor = {})
-
if constructor.is_a?(Hash)
-
super()
-
update(constructor)
-
else
-
super(constructor)
-
end
-
end
-
-
1
def default(key = nil)
-
if key.is_a?(Symbol) && include?(key = key.to_s)
-
self[key]
-
else
-
super
-
end
-
end
-
-
1
def self.new_from_hash_copying_default(hash)
-
new(hash).tap do |new_hash|
-
new_hash.default = hash.default
-
end
-
end
-
-
1
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
-
1
alias_method :regular_update, :update unless method_defined?(:regular_update)
-
-
# Assigns a new value to the hash:
-
#
-
# hash = HashWithIndifferentAccess.new
-
# hash[:key] = "value"
-
#
-
1
def []=(key, value)
-
regular_writer(convert_key(key), convert_value(value))
-
end
-
-
1
alias_method :store, :[]=
-
-
# Updates the instantized hash with values from the second:
-
#
-
# hash_1 = HashWithIndifferentAccess.new
-
# hash_1[:key] = "value"
-
#
-
# hash_2 = HashWithIndifferentAccess.new
-
# hash_2[:key] = "New Value!"
-
#
-
# hash_1.update(hash_2) # => {"key"=>"New Value!"}
-
#
-
1
def update(other_hash)
-
if other_hash.is_a? HashWithIndifferentAccess
-
super(other_hash)
-
else
-
other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
-
self
-
end
-
end
-
-
1
alias_method :merge!, :update
-
-
# Checks the hash for a key matching the argument passed in:
-
#
-
# hash = HashWithIndifferentAccess.new
-
# hash["key"] = "value"
-
# hash.key? :key # => true
-
# hash.key? "key" # => true
-
#
-
1
def key?(key)
-
super(convert_key(key))
-
end
-
-
1
alias_method :include?, :key?
-
1
alias_method :has_key?, :key?
-
1
alias_method :member?, :key?
-
-
# Fetches the value for the specified key, same as doing hash[key]
-
1
def fetch(key, *extras)
-
super(convert_key(key), *extras)
-
end
-
-
# Returns an array of the values at the specified indices:
-
#
-
# hash = HashWithIndifferentAccess.new
-
# hash[:a] = "x"
-
# hash[:b] = "y"
-
# hash.values_at("a", "b") # => ["x", "y"]
-
#
-
1
def values_at(*indices)
-
indices.collect {|key| self[convert_key(key)]}
-
end
-
-
# Returns an exact copy of the hash.
-
1
def dup
-
self.class.new(self).tap do |new_hash|
-
new_hash.default = default
-
end
-
end
-
-
# Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
-
# Does not overwrite the existing hash.
-
1
def merge(hash)
-
self.dup.update(hash)
-
end
-
-
# Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
-
# This overloaded definition prevents returning a regular hash, if reverse_merge is called on a <tt>HashWithDifferentAccess</tt>.
-
1
def reverse_merge(other_hash)
-
super self.class.new_from_hash_copying_default(other_hash)
-
end
-
-
1
def reverse_merge!(other_hash)
-
replace(reverse_merge( other_hash ))
-
end
-
-
# Removes a specified key from the hash.
-
1
def delete(key)
-
super(convert_key(key))
-
end
-
-
1
def stringify_keys!; self end
-
1
def stringify_keys; dup end
-
1
undef :symbolize_keys!
-
1
def symbolize_keys; to_hash.symbolize_keys end
-
1
def to_options!; self end
-
-
# Convert to a Hash with String keys.
-
1
def to_hash
-
Hash.new(default).merge!(self)
-
end
-
-
1
protected
-
1
def convert_key(key)
-
key.kind_of?(Symbol) ? key.to_s : key
-
end
-
-
1
def convert_value(value)
-
if value.is_a? Hash
-
value.nested_under_indifferent_access
-
elsif value.is_a?(Array)
-
value.dup.replace(value.map { |e| convert_value(e) })
-
else
-
value
-
end
-
end
-
end
-
end
-
-
1
HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess
-
1
require 'active_support/json/decoding'
-
1
require 'active_support/json/encoding'
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'active_support/core_ext/module/delegation'
-
1
require 'multi_json'
-
-
1
module ActiveSupport
-
# Look for and parse json strings that look like ISO 8601 times.
-
1
mattr_accessor :parse_json_times
-
-
1
module JSON
-
1
class << self
-
1
def decode(json, options ={})
-
data = MultiJson.decode(json, options)
-
if ActiveSupport.parse_json_times
-
convert_dates_from(data)
-
else
-
data
-
end
-
end
-
-
1
def engine
-
MultiJson.engine
-
end
-
1
alias :backend :engine
-
-
1
def engine=(name)
-
MultiJson.engine = name
-
end
-
1
alias :backend= :engine=
-
-
1
def with_backend(name)
-
old_backend, self.backend = backend, name
-
yield
-
ensure
-
self.backend = old_backend
-
end
-
-
1
def parse_error
-
MultiJson::DecodeError
-
end
-
-
1
private
-
-
1
def convert_dates_from(data)
-
case data
-
when nil
-
nil
-
when DATE_REGEX
-
begin
-
DateTime.parse(data)
-
rescue ArgumentError
-
data
-
end
-
when Array
-
data.map! { |d| convert_dates_from(d) }
-
when Hash
-
data.each do |key, value|
-
data[key] = convert_dates_from(value)
-
end
-
else
-
data
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/to_json'
-
1
require 'active_support/core_ext/module/delegation'
-
1
require 'active_support/deprecation'
-
1
require 'active_support/json/variable'
-
1
require 'active_support/ordered_hash'
-
-
1
require 'bigdecimal'
-
1
require 'active_support/core_ext/big_decimal/conversions' # for #to_s
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/hash/slice'
-
1
require 'active_support/core_ext/object/instance_variables'
-
1
require 'time'
-
1
require 'active_support/core_ext/time/conversions'
-
1
require 'active_support/core_ext/date_time/conversions'
-
1
require 'active_support/core_ext/date/conversions'
-
-
1
module ActiveSupport
-
1
class << self
-
1
delegate :use_standard_json_time_format, :use_standard_json_time_format=,
-
:escape_html_entities_in_json, :escape_html_entities_in_json=,
-
:to => :'ActiveSupport::JSON::Encoding'
-
end
-
-
1
module JSON
-
# matches YAML-formatted dates
-
1
DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[T \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?))$/
-
-
# Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
-
1
def self.encode(value, options = nil)
-
Encoding::Encoder.new(options).encode(value)
-
end
-
-
1
module Encoding #:nodoc:
-
1
class CircularReferenceError < StandardError; end
-
-
1
class Encoder
-
1
attr_reader :options
-
-
1
def initialize(options = nil)
-
@options = options
-
@seen = []
-
end
-
-
1
def encode(value, use_options = true)
-
check_for_circular_references(value) do
-
jsonified = use_options ? value.as_json(options_for(value)) : value.as_json
-
jsonified.encode_json(self)
-
end
-
end
-
-
# like encode, but only calls as_json, without encoding to string
-
1
def as_json(value)
-
check_for_circular_references(value) do
-
value.as_json(options_for(value))
-
end
-
end
-
-
1
def options_for(value)
-
if value.is_a?(Array) || value.is_a?(Hash)
-
# hashes and arrays need to get encoder in the options, so that they can detect circular references
-
(options || {}).merge(:encoder => self)
-
else
-
options
-
end
-
end
-
-
1
def escape(string)
-
Encoding.escape(string)
-
end
-
-
1
private
-
1
def check_for_circular_references(value)
-
if @seen.any? { |object| object.equal?(value) }
-
raise CircularReferenceError, 'object references itself'
-
end
-
@seen.unshift value
-
yield
-
ensure
-
@seen.shift
-
end
-
end
-
-
-
1
ESCAPED_CHARS = {
-
"\x00" => '\u0000', "\x01" => '\u0001', "\x02" => '\u0002',
-
"\x03" => '\u0003', "\x04" => '\u0004', "\x05" => '\u0005',
-
"\x06" => '\u0006', "\x07" => '\u0007', "\x0B" => '\u000B',
-
"\x0E" => '\u000E', "\x0F" => '\u000F', "\x10" => '\u0010',
-
"\x11" => '\u0011', "\x12" => '\u0012', "\x13" => '\u0013',
-
"\x14" => '\u0014', "\x15" => '\u0015', "\x16" => '\u0016',
-
"\x17" => '\u0017', "\x18" => '\u0018', "\x19" => '\u0019',
-
"\x1A" => '\u001A', "\x1B" => '\u001B', "\x1C" => '\u001C',
-
"\x1D" => '\u001D', "\x1E" => '\u001E', "\x1F" => '\u001F',
-
"\010" => '\b',
-
"\f" => '\f',
-
"\n" => '\n',
-
"\r" => '\r',
-
"\t" => '\t',
-
'"' => '\"',
-
'\\' => '\\\\',
-
'>' => '\u003E',
-
'<' => '\u003C',
-
'&' => '\u0026' }
-
-
1
class << self
-
# If true, use ISO 8601 format for dates and times. Otherwise, fall back to the Active Support legacy format.
-
1
attr_accessor :use_standard_json_time_format
-
-
1
attr_accessor :escape_regex
-
1
attr_reader :escape_html_entities_in_json
-
-
1
def escape_html_entities_in_json=(value)
-
1
self.escape_regex = \
-
if @escape_html_entities_in_json = value
-
/[\x00-\x1F"\\><&]/
-
else
-
1
/[\x00-\x1F"\\]/
-
end
-
end
-
-
1
def escape(string)
-
if string.respond_to?(:force_encoding)
-
string = string.encode(::Encoding::UTF_8, :undef => :replace).force_encoding(::Encoding::BINARY)
-
end
-
json = string.
-
gsub(escape_regex) { |s| ESCAPED_CHARS[s] }.
-
gsub(/([\xC0-\xDF][\x80-\xBF]|
-
[\xE0-\xEF][\x80-\xBF]{2}|
-
[\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s|
-
s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/n, '\\\\u\&')
-
}
-
json = %("#{json}")
-
json.force_encoding(::Encoding::UTF_8) if json.respond_to?(:force_encoding)
-
json
-
end
-
end
-
-
1
self.use_standard_json_time_format = true
-
1
self.escape_html_entities_in_json = false
-
end
-
-
1
CircularReferenceError = Deprecation::DeprecatedConstantProxy.new('ActiveSupport::JSON::CircularReferenceError', Encoding::CircularReferenceError)
-
end
-
end
-
-
1
class Object
-
1
def as_json(options = nil) #:nodoc:
-
if respond_to?(:to_hash)
-
to_hash
-
else
-
instance_values
-
end
-
end
-
end
-
-
1
class Struct
-
1
def as_json(options = nil) #:nodoc:
-
Hash[members.zip(values)]
-
end
-
end
-
-
1
class TrueClass
-
1
AS_JSON = ActiveSupport::JSON::Variable.new('true').freeze
-
1
def as_json(options = nil) AS_JSON end #:nodoc:
-
end
-
-
1
class FalseClass
-
1
AS_JSON = ActiveSupport::JSON::Variable.new('false').freeze
-
1
def as_json(options = nil) AS_JSON end #:nodoc:
-
end
-
-
1
class NilClass
-
1
AS_JSON = ActiveSupport::JSON::Variable.new('null').freeze
-
1
def as_json(options = nil) AS_JSON end #:nodoc:
-
end
-
-
1
class String
-
1
def as_json(options = nil) self end #:nodoc:
-
1
def encode_json(encoder) encoder.escape(self) end #:nodoc:
-
end
-
-
1
class Symbol
-
1
def as_json(options = nil) to_s end #:nodoc:
-
end
-
-
1
class Numeric
-
1
def as_json(options = nil) self end #:nodoc:
-
1
def encode_json(encoder) to_s end #:nodoc:
-
end
-
-
1
class BigDecimal
-
# A BigDecimal would be naturally represented as a JSON number. Most libraries,
-
# however, parse non-integer JSON numbers directly as floats. Clients using
-
# those libraries would get in general a wrong number and no way to recover
-
# other than manually inspecting the string with the JSON code itself.
-
#
-
# That's why a JSON string is returned. The JSON literal is not numeric, but if
-
# the other end knows by contract that the data is supposed to be a BigDecimal,
-
# it still has the chance to post-process the string and get the real value.
-
1
def as_json(options = nil) to_s end #:nodoc:
-
end
-
-
1
class Regexp
-
1
def as_json(options = nil) to_s end #:nodoc:
-
end
-
-
1
module Enumerable
-
1
def as_json(options = nil) #:nodoc:
-
to_a.as_json(options)
-
end
-
end
-
-
1
class Array
-
1
def as_json(options = nil) #:nodoc:
-
# use encoder as a proxy to call as_json on all elements, to protect from circular references
-
encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options)
-
map { |v| encoder.as_json(v) }
-
end
-
-
1
def encode_json(encoder) #:nodoc:
-
# we assume here that the encoder has already run as_json on self and the elements, so we run encode_json directly
-
"[#{map { |v| v.encode_json(encoder) } * ','}]"
-
end
-
end
-
-
1
class Hash
-
1
def as_json(options = nil) #:nodoc:
-
# create a subset of the hash by applying :only or :except
-
subset = if options
-
if attrs = options[:only]
-
slice(*Array.wrap(attrs))
-
elsif attrs = options[:except]
-
except(*Array.wrap(attrs))
-
else
-
self
-
end
-
else
-
self
-
end
-
-
# use encoder as a proxy to call as_json on all values in the subset, to protect from circular references
-
encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options)
-
result = self.is_a?(ActiveSupport::OrderedHash) ? ActiveSupport::OrderedHash : Hash
-
result[subset.map { |k, v| [k.to_s, encoder.as_json(v)] }]
-
end
-
-
1
def encode_json(encoder)
-
# values are encoded with use_options = false, because we don't want hash representations from ActiveModel to be
-
# processed once again with as_json with options, as this could cause unexpected results (i.e. missing fields);
-
-
# on the other hand, we need to run as_json on the elements, because the model representation may contain fields
-
# like Time/Date in their original (not jsonified) form, etc.
-
-
"{#{map { |k,v| "#{encoder.encode(k.to_s)}:#{encoder.encode(v, false)}" } * ','}}"
-
end
-
end
-
-
1
class Time
-
1
def as_json(options = nil) #:nodoc:
-
if ActiveSupport.use_standard_json_time_format
-
xmlschema
-
else
-
%(#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
-
end
-
end
-
end
-
-
1
class Date
-
1
def as_json(options = nil) #:nodoc:
-
if ActiveSupport.use_standard_json_time_format
-
strftime("%Y-%m-%d")
-
else
-
strftime("%Y/%m/%d")
-
end
-
end
-
end
-
-
1
class DateTime
-
1
def as_json(options = nil) #:nodoc:
-
if ActiveSupport.use_standard_json_time_format
-
xmlschema
-
else
-
strftime('%Y/%m/%d %H:%M:%S %z')
-
end
-
end
-
end
-
1
module ActiveSupport
-
1
module JSON
-
# A string that returns itself as its JSON-encoded form.
-
1
class Variable < String
-
1
def as_json(options = nil) self end #:nodoc:
-
1
def encode_json(encoder) self end #:nodoc:
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'active_support/core_ext/class/attribute'
-
-
1
module ActiveSupport
-
# ActiveSupport::LogSubscriber is an object set to consume ActiveSupport::Notifications
-
# with the sole purpose of logging them. The log subscriber dispatches notifications to
-
# a registered object based on its given namespace.
-
#
-
# An example would be Active Record log subscriber responsible for logging queries:
-
#
-
# module ActiveRecord
-
# class LogSubscriber < ActiveSupport::LogSubscriber
-
# def sql(event)
-
# "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}"
-
# end
-
# end
-
# end
-
#
-
# And it's finally registered as:
-
#
-
# ActiveRecord::LogSubscriber.attach_to :active_record
-
#
-
# Since we need to know all instance methods before attaching the log subscriber,
-
# the line above should be called after your <tt>ActiveRecord::LogSubscriber</tt> definition.
-
#
-
# After configured, whenever a "sql.active_record" notification is published,
-
# it will properly dispatch the event (ActiveSupport::Notifications::Event) to
-
# the sql method.
-
#
-
# Log subscriber also has some helpers to deal with logging and automatically flushes
-
# all logs when the request finishes (via action_dispatch.callback notification) in
-
# a Rails environment.
-
1
class LogSubscriber
-
# Embed in a String to clear all previous ANSI sequences.
-
1
CLEAR = "\e[0m"
-
1
BOLD = "\e[1m"
-
-
# Colors
-
1
BLACK = "\e[30m"
-
1
RED = "\e[31m"
-
1
GREEN = "\e[32m"
-
1
YELLOW = "\e[33m"
-
1
BLUE = "\e[34m"
-
1
MAGENTA = "\e[35m"
-
1
CYAN = "\e[36m"
-
1
WHITE = "\e[37m"
-
-
1
mattr_accessor :colorize_logging
-
1
self.colorize_logging = true
-
-
1
class_attribute :logger
-
-
1
class << self
-
1
remove_method :logger
-
1
def logger
-
@logger ||= Rails.logger if defined?(Rails)
-
end
-
-
1
def attach_to(namespace, log_subscriber=new, notifier=ActiveSupport::Notifications)
-
3
log_subscribers << log_subscriber
-
3
@@flushable_loggers = nil
-
-
3
log_subscriber.public_methods(false).each do |event|
-
20
next if 'call' == event.to_s
-
-
20
notifier.subscribe("#{event}.#{namespace}", log_subscriber)
-
end
-
end
-
-
1
def log_subscribers
-
3
@@log_subscribers ||= []
-
end
-
-
1
def flushable_loggers
-
@@flushable_loggers ||= begin
-
loggers = log_subscribers.map(&:logger)
-
loggers.uniq!
-
loggers.select { |l| l.respond_to?(:flush) }
-
end
-
end
-
-
# Flush all log_subscribers' logger.
-
1
def flush_all!
-
flushable_loggers.each { |log| log.flush }
-
end
-
end
-
-
1
def call(message, *args)
-
9
return unless logger
-
-
9
method = message.split('.').first
-
9
begin
-
9
send(method, ActiveSupport::Notifications::Event.new(message, *args))
-
rescue Exception => e
-
logger.error "Could not log #{message.inspect} event. #{e.class}: #{e.message}"
-
end
-
end
-
-
1
protected
-
-
1
%w(info debug warn error fatal unknown).each do |level|
-
6
class_eval <<-METHOD, __FILE__, __LINE__ + 1
-
def #{level}(*args, &block)
-
return unless logger
-
logger.#{level}(*args, &block)
-
end
-
METHOD
-
end
-
-
# Set color by using a string or one of the defined constants. If a third
-
# option is set to true, it also adds bold to the string. This is based
-
# on the Highline implementation and will automatically append CLEAR to the
-
# end of the returned String.
-
#
-
1
def color(text, color, bold=false)
-
return text unless colorize_logging
-
color = self.class.const_get(color.to_s.upcase) if color.is_a?(Symbol)
-
bold = bold ? BOLD : ""
-
"#{bold}#{color}#{text}#{CLEAR}"
-
end
-
end
-
end
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
1
require 'active_support/core_ext/module/aliasing'
-
-
1
module ActiveSupport
-
1
module Memoizable
-
1
def self.memoized_ivar_for(symbol)
-
3
"@_memoized_#{symbol.to_s.sub(/\?\Z/, '_query').sub(/!\Z/, '_bang')}".to_sym
-
end
-
-
1
module InstanceMethods
-
1
def self.included(base)
-
3
base.class_eval do
-
3
unless base.method_defined?(:freeze_without_memoizable)
-
2
alias_method_chain :freeze, :memoizable
-
end
-
end
-
end
-
-
1
def freeze_with_memoizable
-
memoize_all unless frozen?
-
freeze_without_memoizable
-
end
-
-
1
def memoize_all
-
prime_cache ".*"
-
end
-
-
1
def unmemoize_all
-
flush_cache ".*"
-
end
-
-
1
def prime_cache(*syms)
-
syms.each do |sym|
-
methods.each do |m|
-
if m.to_s =~ /^_unmemoized_(#{sym})/
-
if method(m).arity == 0
-
__send__($1)
-
else
-
ivar = ActiveSupport::Memoizable.memoized_ivar_for($1)
-
instance_variable_set(ivar, {})
-
end
-
end
-
end
-
end
-
end
-
-
1
def flush_cache(*syms)
-
syms.each do |sym|
-
(methods + private_methods + protected_methods).each do |m|
-
if m.to_s =~ /^_unmemoized_(#{sym.to_s.gsub(/\?\Z/, '\?')})/
-
ivar = ActiveSupport::Memoizable.memoized_ivar_for($1)
-
instance_variable_get(ivar).clear if instance_variable_defined?(ivar)
-
end
-
end
-
end
-
end
-
end
-
-
1
def memoize(*symbols)
-
3
symbols.each do |symbol|
-
3
original_method = :"_unmemoized_#{symbol}"
-
3
memoized_ivar = ActiveSupport::Memoizable.memoized_ivar_for(symbol)
-
-
3
class_eval <<-EOS, __FILE__, __LINE__ + 1
-
include InstanceMethods # include InstanceMethods
-
#
-
if method_defined?(:#{original_method}) # if method_defined?(:_unmemoized_mime_type)
-
raise "Already memoized #{symbol}" # raise "Already memoized mime_type"
-
end # end
-
alias #{original_method} #{symbol} # alias _unmemoized_mime_type mime_type
-
#
-
if instance_method(:#{symbol}).arity == 0 # if instance_method(:mime_type).arity == 0
-
def #{symbol}(reload = false) # def mime_type(reload = false)
-
if reload || !defined?(#{memoized_ivar}) || #{memoized_ivar}.empty? # if reload || !defined?(@_memoized_mime_type) || @_memoized_mime_type.empty?
-
#{memoized_ivar} = [#{original_method}] # @_memoized_mime_type = [_unmemoized_mime_type]
-
end # end
-
#{memoized_ivar}[0] # @_memoized_mime_type[0]
-
end # end
-
else # else
-
def #{symbol}(*args) # def mime_type(*args)
-
#{memoized_ivar} ||= {} unless frozen? # @_memoized_mime_type ||= {} unless frozen?
-
reload = args.pop if args.last == true || args.last == :reload # reload = args.pop if args.last == true || args.last == :reload
-
#
-
if defined?(#{memoized_ivar}) && #{memoized_ivar} # if defined?(@_memoized_mime_type) && @_memoized_mime_type
-
if !reload && #{memoized_ivar}.has_key?(args) # if !reload && @_memoized_mime_type.has_key?(args)
-
#{memoized_ivar}[args] # @_memoized_mime_type[args]
-
elsif #{memoized_ivar} # elsif @_memoized_mime_type
-
#{memoized_ivar}[args] = #{original_method}(*args) # @_memoized_mime_type[args] = _unmemoized_mime_type(*args)
-
end # end
-
else # else
-
#{original_method}(*args) # _unmemoized_mime_type(*args)
-
end # end
-
end # end
-
end # end
-
#
-
if private_method_defined?(#{original_method.inspect}) # if private_method_defined?(:_unmemoized_mime_type)
-
private #{symbol.inspect} # private :mime_type
-
elsif protected_method_defined?(#{original_method.inspect}) # elsif protected_method_defined?(:_unmemoized_mime_type)
-
protected #{symbol.inspect} # protected :mime_type
-
end # end
-
EOS
-
end
-
end
-
end
-
end
-
1
require 'active_support/secure_random'
-
1
require 'active_support/core_ext/module/delegation'
-
-
1
module ActiveSupport
-
1
module Notifications
-
1
class Instrumenter
-
1
attr_reader :id
-
-
1
def initialize(notifier)
-
1
@id = unique_id
-
1
@notifier = notifier
-
end
-
-
# Instrument the given block by measuring the time taken to execute it
-
# and publish it. Notice that events get sent even if an error occurs
-
# in the passed-in block
-
1
def instrument(name, payload={})
-
9
started = Time.now
-
-
9
begin
-
9
yield
-
rescue Exception => e
-
payload[:exception] = [e.class.name, e.message]
-
raise e
-
ensure
-
9
@notifier.publish(name, started, Time.now, @id, payload)
-
9
end
-
end
-
-
1
private
-
1
def unique_id
-
1
::SecureRandom.hex(10)
-
end
-
end
-
-
1
class Event
-
1
attr_reader :name, :time, :end, :transaction_id, :payload, :duration
-
-
1
def initialize(name, start, ending, transaction_id, payload)
-
9
@name = name
-
9
@payload = payload.dup
-
9
@time = start
-
9
@transaction_id = transaction_id
-
9
@end = ending
-
9
@duration = 1000.0 * (@end - @time)
-
end
-
-
1
def parent_of?(event)
-
start = (time - event.time) * 1000
-
start <= 0 && (start + duration >= event.duration)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/deep_merge'
-
-
1
module ActiveSupport
-
1
class OptionMerger #:nodoc:
-
1
instance_methods.each do |method|
-
109
undef_method(method) if method !~ /^(__|instance_eval|class|object_id)/
-
end
-
-
1
def initialize(context, options)
-
@context, @options = context, options
-
end
-
-
1
private
-
1
def method_missing(method, *arguments, &block)
-
if arguments.last.is_a?(Proc)
-
proc = arguments.pop
-
arguments << lambda { |*args| @options.deep_merge(proc.call(*args)) }
-
else
-
arguments << (arguments.last.respond_to?(:to_hash) ? @options.deep_merge(arguments.pop) : @options.dup)
-
end
-
-
@context.__send__(method, *arguments, &block)
-
end
-
end
-
end
-
1
require 'active_support/concern'
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/proc'
-
1
require 'active_support/core_ext/string/inflections'
-
1
require 'active_support/core_ext/array/extract_options'
-
-
1
module ActiveSupport
-
# Rescuable module adds support for easier exception handling.
-
1
module Rescuable
-
1
extend Concern
-
-
1
included do
-
1
class_attribute :rescue_handlers
-
1
self.rescue_handlers = []
-
end
-
-
1
module ClassMethods
-
# Rescue exceptions raised in controller actions.
-
#
-
# <tt>rescue_from</tt> receives a series of exception classes or class
-
# names, and a trailing <tt>:with</tt> option with the name of a method
-
# or a Proc object to be called to handle them. Alternatively a block can
-
# be given.
-
#
-
# Handlers that take one argument will be called with the exception, so
-
# that the exception can be inspected when dealing with it.
-
#
-
# Handlers are inherited. They are searched from right to left, from
-
# bottom to top, and up the hierarchy. The handler of the first class for
-
# which <tt>exception.is_a?(klass)</tt> holds true is the one invoked, if
-
# any.
-
#
-
# class ApplicationController < ActionController::Base
-
# rescue_from User::NotAuthorized, :with => :deny_access # self defined exception
-
# rescue_from ActiveRecord::RecordInvalid, :with => :show_errors
-
#
-
# rescue_from 'MyAppError::Base' do |exception|
-
# render :xml => exception, :status => 500
-
# end
-
#
-
# protected
-
# def deny_access
-
# ...
-
# end
-
#
-
# def show_errors(exception)
-
# exception.record.new_record? ? ...
-
# end
-
# end
-
#
-
1
def rescue_from(*klasses, &block)
-
options = klasses.extract_options!
-
-
unless options.has_key?(:with)
-
if block_given?
-
options[:with] = block
-
else
-
raise ArgumentError, "Need a handler. Supply an options hash that has a :with key as the last argument."
-
end
-
end
-
-
klasses.each do |klass|
-
key = if klass.is_a?(Class) && klass <= Exception
-
klass.name
-
elsif klass.is_a?(String)
-
klass
-
else
-
raise ArgumentError, "#{klass} is neither an Exception nor a String"
-
end
-
-
# put the new handler at the end because the list is read in reverse
-
self.rescue_handlers += [[key, options[:with]]]
-
end
-
end
-
end
-
-
# Tries to rescue the exception by looking up and calling a registered handler.
-
1
def rescue_with_handler(exception)
-
if handler = handler_for_rescue(exception)
-
handler.arity != 0 ? handler.call(exception) : handler.call
-
true # don't rely on the return value of the handler
-
end
-
end
-
-
1
def handler_for_rescue(exception)
-
# We go from right to left because pairs are pushed onto rescue_handlers
-
# as rescue_from declarations are found.
-
_, rescuer = self.class.rescue_handlers.reverse.detect do |klass_name, handler|
-
# The purpose of allowing strings in rescue_from is to support the
-
# declaration of handler associations for exception classes whose
-
# definition is yet unknown.
-
#
-
# Since this loop needs the constants it would be inconsistent to
-
# assume they should exist at this point. An early raised exception
-
# could trigger some other handler and the array could include
-
# precisely a string whose corresponding constant has not yet been
-
# seen. This is why we are tolerant to unknown constants.
-
#
-
# Note that this tolerance only matters if the exception was given as
-
# a string, otherwise a NameError will be raised by the interpreter
-
# itself when rescue_from CONSTANT is executed.
-
klass = self.class.const_get(klass_name) rescue nil
-
klass ||= klass_name.constantize rescue nil
-
exception.is_a?(klass) if klass
-
end
-
-
case rescuer
-
when Symbol
-
method(rescuer)
-
when Proc
-
rescuer.bind(self)
-
end
-
end
-
end
-
end
-
# Backported Ruby builtins so you can code with the latest & greatest
-
# but still run on any Ruby 1.8.x.
-
#
-
# Date next_year, next_month
-
# DateTime to_date, to_datetime, xmlschema
-
# Enumerable group_by, each_with_object, none?
-
# Process Process.daemon
-
# REXML security fix
-
# String ord
-
# Time to_date, to_time, to_datetime
-
1
require 'active_support'
-
1
require 'active_support/core_ext/date/calculations'
-
1
require 'active_support/core_ext/date_time/conversions'
-
1
require 'active_support/core_ext/enumerable'
-
1
require 'active_support/core_ext/process/daemon'
-
1
require 'active_support/core_ext/string/conversions'
-
1
require 'active_support/core_ext/string/interpolation'
-
1
require 'active_support/core_ext/string/encoding'
-
1
require 'active_support/core_ext/rexml'
-
1
require 'active_support/core_ext/time/conversions'
-
1
require 'active_support/core_ext/file/path'
-
1
require 'active_support/core_ext/module/method_names'
-
1
require 'active_support/deprecation'
-
-
1
module ActiveSupport
-
# Use Ruby's SecureRandom library.
-
1
SecureRandom = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('ActiveSupport::SecureRandom', ::SecureRandom) # :nodoc:
-
end
-
1
module ActiveSupport
-
# Wrapping a string in this class gives you a prettier way to test
-
# for equality. The value returned by <tt>Rails.env</tt> is wrapped
-
# in a StringInquirer object so instead of calling this:
-
#
-
# Rails.env == "production"
-
#
-
# you can call this:
-
#
-
# Rails.env.production?
-
#
-
1
class StringInquirer < String
-
1
def method_missing(method_name, *arguments)
-
5
if method_name.to_s[-1,1] == "?"
-
5
self == method_name.to_s[0..-2]
-
else
-
super
-
end
-
end
-
end
-
end
-
1
require 'test/unit/testcase'
-
1
require 'active_support/testing/setup_and_teardown'
-
1
require 'active_support/testing/assertions'
-
1
require 'active_support/testing/deprecation'
-
1
require 'active_support/testing/declarative'
-
1
require 'active_support/testing/pending'
-
1
require 'active_support/testing/isolation'
-
1
require 'active_support/testing/mochaing'
-
1
require 'active_support/core_ext/kernel/reporting'
-
-
1
module ActiveSupport
-
1
class TestCase < ::Test::Unit::TestCase
-
1
if defined? MiniTest
-
1
Assertion = MiniTest::Assertion
-
1
alias_method :method_name, :name if method_defined? :name
-
1
alias_method :method_name, :__name__ if method_defined? :__name__
-
else
-
Assertion = Test::Unit::AssertionFailedError
-
-
undef :default_test
-
end
-
-
1
$tags = {}
-
1
def self.for_tag(tag)
-
yield if $tags[tag]
-
end
-
-
1
include ActiveSupport::Testing::SetupAndTeardown
-
1
include ActiveSupport::Testing::Assertions
-
1
include ActiveSupport::Testing::Deprecation
-
1
include ActiveSupport::Testing::Pending
-
1
extend ActiveSupport::Testing::Declarative
-
end
-
end
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_support/core_ext/object/blank'
-
-
1
module ActiveSupport
-
1
module Testing
-
1
module Assertions
-
# Test numeric difference between the return value of an expression as a result of what is evaluated
-
# in the yielded block.
-
#
-
# assert_difference 'Article.count' do
-
# post :create, :article => {...}
-
# end
-
#
-
# An arbitrary expression is passed in and evaluated.
-
#
-
# assert_difference 'assigns(:article).comments(:reload).size' do
-
# post :create, :comment => {...}
-
# end
-
#
-
# An arbitrary positive or negative difference can be specified. The default is +1.
-
#
-
# assert_difference 'Article.count', -1 do
-
# post :delete, :id => ...
-
# end
-
#
-
# An array of expressions can also be passed in and evaluated.
-
#
-
# assert_difference [ 'Article.count', 'Post.count' ], +2 do
-
# post :create, :article => {...}
-
# end
-
#
-
# A lambda or a list of lambdas can be passed in and evaluated:
-
#
-
# assert_difference lambda { Article.count }, 2 do
-
# post :create, :article => {...}
-
# end
-
#
-
# assert_difference [->{ Article.count }, ->{ Post.count }], 2 do
-
# post :create, :article => {...}
-
# end
-
#
-
# A error message can be specified.
-
#
-
# assert_difference 'Article.count', -1, "An Article should be destroyed" do
-
# post :delete, :id => ...
-
# end
-
1
def assert_difference(expression, difference = 1, message = nil, &block)
-
expressions = Array.wrap expression
-
-
exps = expressions.map { |e|
-
e.respond_to?(:call) ? e : lambda { eval(e, block.binding) }
-
}
-
before = exps.map { |e| e.call }
-
-
yield
-
-
expressions.zip(exps).each_with_index do |(code, e), i|
-
error = "#{code.inspect} didn't change by #{difference}"
-
error = "#{message}.\n#{error}" if message
-
assert_equal(before[i] + difference, e.call, error)
-
end
-
end
-
-
# Assertion that the numeric result of evaluating an expression is not changed before and after
-
# invoking the passed in block.
-
#
-
# assert_no_difference 'Article.count' do
-
# post :create, :article => invalid_attributes
-
# end
-
#
-
# A error message can be specified.
-
#
-
# assert_no_difference 'Article.count', "An Article should not be destroyed" do
-
# post :create, :article => invalid_attributes
-
# end
-
1
def assert_no_difference(expression, message = nil, &block)
-
assert_difference expression, 0, message, &block
-
end
-
-
# Test if an expression is blank. Passes if object.blank? is true.
-
#
-
# assert_blank [] # => true
-
1
def assert_blank(object, message=nil)
-
message ||= "#{object.inspect} is not blank"
-
assert object.blank?, message
-
end
-
-
# Test if an expression is not blank. Passes if object.present? is true.
-
#
-
# assert_present {:data => 'x' } # => true
-
1
def assert_present(object, message=nil)
-
message ||= "#{object.inspect} is blank"
-
assert object.present?, message
-
end
-
end
-
end
-
end
-
1
module ActiveSupport
-
1
module Testing
-
1
module Declarative
-
-
1
def self.extended(klass)
-
1
klass.class_eval do
-
-
1
unless method_defined?(:describe)
-
1
def self.describe(text)
-
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
def self.name
-
"#{text}"
-
end
-
RUBY_EVAL
-
end
-
end
-
-
end
-
end
-
-
1
unless defined?(Spec)
-
# test "verify something" do
-
# ...
-
# end
-
def test(name, &block)
-
test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
-
defined = instance_method(test_name) rescue false
-
raise "#{test_name} is already defined in #{self}" if defined
-
if block_given?
-
define_method(test_name, &block)
-
else
-
define_method(test_name) do
-
flunk "No implementation provided for #{name}"
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/deprecation'
-
-
1
module ActiveSupport
-
1
module Testing
-
1
module Deprecation #:nodoc:
-
1
def assert_deprecated(match = nil, &block)
-
result, warnings = collect_deprecations(&block)
-
assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
-
if match
-
match = Regexp.new(Regexp.escape(match)) unless match.is_a?(Regexp)
-
assert warnings.any? { |w| w =~ match }, "No deprecation warning matched #{match}: #{warnings.join(', ')}"
-
end
-
result
-
end
-
-
1
def assert_not_deprecated(&block)
-
result, deprecations = collect_deprecations(&block)
-
assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}"
-
result
-
end
-
-
1
private
-
1
def collect_deprecations
-
old_behavior = ActiveSupport::Deprecation.behavior
-
deprecations = []
-
ActiveSupport::Deprecation.behavior = Proc.new do |message, callstack|
-
deprecations << message
-
end
-
result = yield
-
[result, deprecations]
-
ensure
-
ActiveSupport::Deprecation.behavior = old_behavior
-
end
-
end
-
end
-
end
-
-
1
begin
-
1
require 'test/unit/error'
-
rescue LoadError
-
# Using miniunit, ignore.
-
else
-
module Test
-
module Unit
-
class Error #:nodoc:
-
# Silence warnings when reporting test errors.
-
def message_with_silenced_deprecation
-
ActiveSupport::Deprecation.silence { message_without_silenced_deprecation }
-
end
-
alias_method :message_without_silenced_deprecation, :message
-
alias_method :message, :message_with_silenced_deprecation
-
end
-
end
-
end
-
end
-
1
require 'rbconfig'
-
1
module ActiveSupport
-
1
module Testing
-
1
class RemoteError < StandardError
-
-
1
attr_reader :message, :backtrace
-
-
1
def initialize(exception)
-
@message = "caught #{exception.class.name}: #{exception.message}"
-
@backtrace = exception.backtrace
-
end
-
end
-
-
1
class ProxyTestResult
-
1
def initialize
-
@calls = []
-
end
-
-
1
def add_error(e)
-
e = Test::Unit::Error.new(e.test_name, RemoteError.new(e.exception))
-
@calls << [:add_error, e]
-
end
-
-
1
def __replay__(result)
-
@calls.each do |name, args|
-
result.send(name, *args)
-
end
-
end
-
-
1
def method_missing(name, *args)
-
@calls << [name, args]
-
end
-
end
-
-
1
module Isolation
-
1
def self.forking_env?
-
1
!ENV["NO_FORK"] && ((RbConfig::CONFIG['host_os'] !~ /mswin|mingw/) && (RUBY_PLATFORM !~ /java/))
-
end
-
-
1
def self.included(base)
-
if defined?(::MiniTest) && base < ::MiniTest::Unit::TestCase
-
base.send :include, MiniTest
-
elsif defined?(Test::Unit)
-
base.send :include, TestUnit
-
end
-
end
-
-
1
def _run_class_setup # class setup method should only happen in parent
-
unless defined?(@@ran_class_setup) || ENV['ISOLATION_TEST']
-
self.class.setup if self.class.respond_to?(:setup)
-
@@ran_class_setup = true
-
end
-
end
-
-
1
module TestUnit
-
1
def run(result)
-
_run_class_setup
-
-
yield(Test::Unit::TestCase::STARTED, name)
-
-
@_result = result
-
-
serialized = run_in_isolation do |proxy|
-
begin
-
super(proxy) { }
-
rescue Exception => e
-
proxy.add_error(Test::Unit::Error.new(name, e))
-
end
-
end
-
-
retval, proxy = Marshal.load(serialized)
-
proxy.__replay__(@_result)
-
-
yield(Test::Unit::TestCase::FINISHED, name)
-
retval
-
end
-
end
-
-
1
module MiniTest
-
1
def run(runner)
-
_run_class_setup
-
-
serialized = run_in_isolation do |isolated_runner|
-
super(isolated_runner)
-
end
-
-
retval, proxy = Marshal.load(serialized)
-
proxy.__replay__(runner)
-
retval
-
end
-
end
-
-
1
module Forking
-
1
def run_in_isolation(&blk)
-
read, write = IO.pipe
-
-
pid = fork do
-
read.close
-
proxy = ProxyTestResult.new
-
retval = yield proxy
-
write.puts [Marshal.dump([retval, proxy])].pack("m")
-
exit!
-
end
-
-
write.close
-
result = read.read
-
Process.wait2(pid)
-
return result.unpack("m")[0]
-
end
-
end
-
-
1
module Subprocess
-
1
ORIG_ARGV = ARGV.dup unless defined?(ORIG_ARGV)
-
-
# Crazy H4X to get this working in windows / jruby with
-
# no forking.
-
1
def run_in_isolation(&blk)
-
require "tempfile"
-
-
if ENV["ISOLATION_TEST"]
-
proxy = ProxyTestResult.new
-
retval = yield proxy
-
File.open(ENV["ISOLATION_OUTPUT"], "w") do |file|
-
file.puts [Marshal.dump([retval, proxy])].pack("m")
-
end
-
exit!
-
else
-
Tempfile.open("isolation") do |tmpfile|
-
ENV["ISOLATION_TEST"] = @method_name
-
ENV["ISOLATION_OUTPUT"] = tmpfile.path
-
-
load_paths = $-I.map {|p| "-I\"#{File.expand_path(p)}\"" }.join(" ")
-
`#{Gem.ruby} #{load_paths} #{$0} #{ORIG_ARGV.join(" ")} -t\"#{self.class}\"`
-
-
ENV.delete("ISOLATION_TEST")
-
ENV.delete("ISOLATION_OUTPUT")
-
-
return tmpfile.read.unpack("m")[0]
-
end
-
end
-
end
-
end
-
-
1
include forking_env? ? Forking : Subprocess
-
end
-
end
-
end
-
-
# Only in subprocess for windows / jruby.
-
1
if ENV['ISOLATION_TEST']
-
require "test/unit/collector/objectspace"
-
class Test::Unit::Collector::ObjectSpace
-
def include?(test)
-
super && test.method_name == ENV['ISOLATION_TEST']
-
end
-
end
-
end
-
1
begin
-
2
silence_warnings { require 'mocha' }
-
rescue LoadError
-
# Fake Mocha::ExpectationError so we can rescue it in #run. Bleh.
-
1
Object.const_set :Mocha, Module.new
-
1
Mocha.const_set :ExpectationError, Class.new(StandardError)
-
end
-
# Some code from jeremymcanally's "pending"
-
# https://github.com/jeremymcanally/pending/tree/master
-
-
1
module ActiveSupport
-
1
module Testing
-
1
module Pending
-
-
1
unless defined?(Spec)
-
-
@@pending_cases = []
-
@@at_exit = false
-
-
def pending(description = "", &block)
-
if defined?(::MiniTest)
-
skip(description.blank? ? nil : description)
-
else
-
if description.is_a?(Symbol)
-
is_pending = $tags[description]
-
return block.call unless is_pending
-
end
-
-
if block_given?
-
failed = false
-
-
begin
-
block.call
-
rescue Exception
-
failed = true
-
end
-
-
flunk("<#{description}> did not fail.") unless failed
-
end
-
-
caller[0] =~ (/(.*):(.*):in `(.*)'/)
-
@@pending_cases << "#{$3} at #{$1}, line #{$2}"
-
print "P"
-
-
@@at_exit ||= begin
-
at_exit do
-
puts "\nPending Cases:"
-
@@pending_cases.each do |test_case|
-
puts test_case
-
end
-
end
-
end
-
end
-
end
-
end
-
-
end
-
end
-
end
-
1
require 'active_support/concern'
-
1
require 'active_support/callbacks'
-
-
1
module ActiveSupport
-
1
module Testing
-
1
module SetupAndTeardown
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
include ActiveSupport::Callbacks
-
1
define_callbacks :setup, :teardown
-
-
1
if defined?(MiniTest::Assertions) && TestCase < MiniTest::Assertions
-
1
include ForMiniTest
-
else
-
include ForClassicTestUnit
-
end
-
end
-
-
1
module ClassMethods
-
1
def setup(*args, &block)
-
8
set_callback(:setup, :before, *args, &block)
-
end
-
-
1
def teardown(*args, &block)
-
3
set_callback(:teardown, :after, *args, &block)
-
end
-
end
-
-
1
module ForMiniTest
-
1
def run(runner)
-
result = '.'
-
begin
-
run_callbacks :setup do
-
result = super
-
end
-
rescue Exception => e
-
result = runner.puke(self.class, method_name, e)
-
ensure
-
begin
-
run_callbacks :teardown
-
rescue Exception => e
-
result = runner.puke(self.class, method_name, e)
-
end
-
end
-
result
-
end
-
end
-
-
1
module ForClassicTestUnit
-
# For compatibility with Ruby < 1.8.6
-
1
PASSTHROUGH_EXCEPTIONS = Test::Unit::TestCase::PASSTHROUGH_EXCEPTIONS rescue [NoMemoryError, SignalException, Interrupt, SystemExit]
-
-
# This redefinition is unfortunate but test/unit shows us no alternative.
-
# Doubly unfortunate: hax to support Mocha's hax.
-
1
def run(result)
-
return if @method_name.to_s == "default_test"
-
-
mocha_counter = retrieve_mocha_counter(result)
-
yield(Test::Unit::TestCase::STARTED, name)
-
@_result = result
-
-
begin
-
begin
-
run_callbacks :setup do
-
setup
-
__send__(@method_name)
-
mocha_verify(mocha_counter) if mocha_counter
-
end
-
rescue Mocha::ExpectationError => e
-
add_failure(e.message, e.backtrace)
-
rescue Test::Unit::AssertionFailedError => e
-
add_failure(e.message, e.backtrace)
-
rescue Exception => e
-
raise if PASSTHROUGH_EXCEPTIONS.include?(e.class)
-
add_error(e)
-
ensure
-
begin
-
teardown
-
run_callbacks :teardown
-
rescue Test::Unit::AssertionFailedError => e
-
add_failure(e.message, e.backtrace)
-
rescue Exception => e
-
raise if PASSTHROUGH_EXCEPTIONS.include?(e.class)
-
add_error(e)
-
end
-
end
-
ensure
-
mocha_teardown if mocha_counter
-
end
-
-
result.add_run
-
yield(Test::Unit::TestCase::FINISHED, name)
-
end
-
-
1
protected
-
-
1
def retrieve_mocha_counter(result) #:nodoc:
-
if respond_to?(:mocha_verify) # using mocha
-
if defined?(Mocha::TestCaseAdapter::AssertionCounter)
-
Mocha::TestCaseAdapter::AssertionCounter.new(result)
-
else
-
Mocha::Integration::TestUnit::AssertionCounter.new(result)
-
end
-
end
-
end
-
end
-
-
end
-
end
-
end
-
1
require 'active_support'
-
-
1
module ActiveSupport
-
1
autoload :Duration, 'active_support/duration'
-
1
autoload :TimeWithZone, 'active_support/time_with_zone'
-
1
autoload :TimeZone, 'active_support/values/time_zone'
-
-
1
on_load_all do
-
[Duration, TimeWithZone, TimeZone]
-
end
-
end
-
-
1
require 'date'
-
1
require 'time'
-
-
1
require 'active_support/core_ext/time/publicize_conversion_methods'
-
1
require 'active_support/core_ext/time/marshal'
-
1
require 'active_support/core_ext/time/acts_like'
-
1
require 'active_support/core_ext/time/calculations'
-
1
require 'active_support/core_ext/time/conversions'
-
1
require 'active_support/core_ext/time/zones'
-
-
1
require 'active_support/core_ext/date/acts_like'
-
1
require 'active_support/core_ext/date/freeze'
-
1
require 'active_support/core_ext/date/calculations'
-
1
require 'active_support/core_ext/date/conversions'
-
1
require 'active_support/core_ext/date/zones'
-
-
1
require 'active_support/core_ext/date_time/acts_like'
-
1
require 'active_support/core_ext/date_time/calculations'
-
1
require 'active_support/core_ext/date_time/conversions'
-
1
require 'active_support/core_ext/date_time/zones'
-
-
1
require 'active_support/core_ext/integer/time'
-
1
require 'active_support/core_ext/numeric/time'
-
1
require "active_support/values/time_zone"
-
1
require 'active_support/core_ext/object/acts_like'
-
1
require 'active_support/core_ext/object/inclusion'
-
-
1
module ActiveSupport
-
# A Time-like class that can represent a time in any time zone. Necessary because standard Ruby Time instances are
-
# limited to UTC and the system's <tt>ENV['TZ']</tt> zone.
-
#
-
# You shouldn't ever need to create a TimeWithZone instance directly via <tt>new</tt> . Instead use methods
-
# +local+, +parse+, +at+ and +now+ on TimeZone instances, and +in_time_zone+ on Time and DateTime instances.
-
# Examples:
-
#
-
# Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
-
# Time.zone.local(2007, 2, 10, 15, 30, 45) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
-
# Time.zone.parse('2007-02-01 15:30:45') # => Sat, 10 Feb 2007 15:30:45 EST -05:00
-
# Time.zone.at(1170361845) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
-
# Time.zone.now # => Sun, 18 May 2008 13:07:55 EDT -04:00
-
# Time.utc(2007, 2, 10, 20, 30, 45).in_time_zone # => Sat, 10 Feb 2007 15:30:45 EST -05:00
-
#
-
# See Time and TimeZone for further documentation of these methods.
-
#
-
# TimeWithZone instances implement the same API as Ruby Time instances, so that Time and TimeWithZone instances are interchangeable.
-
# Examples:
-
#
-
# t = Time.zone.now # => Sun, 18 May 2008 13:27:25 EDT -04:00
-
# t.hour # => 13
-
# t.dst? # => true
-
# t.utc_offset # => -14400
-
# t.zone # => "EDT"
-
# t.to_s(:rfc822) # => "Sun, 18 May 2008 13:27:25 -0400"
-
# t + 1.day # => Mon, 19 May 2008 13:27:25 EDT -04:00
-
# t.beginning_of_year # => Tue, 01 Jan 2008 00:00:00 EST -05:00
-
# t > Time.utc(1999) # => true
-
# t.is_a?(Time) # => true
-
# t.is_a?(ActiveSupport::TimeWithZone) # => true
-
#
-
1
class TimeWithZone
-
1
def self.name
-
'Time' # Report class name as 'Time' to thwart type checking
-
end
-
-
1
include Comparable
-
1
attr_reader :time_zone
-
-
1
def initialize(utc_time, time_zone, local_time = nil, period = nil)
-
@utc, @time_zone, @time = utc_time, time_zone, local_time
-
@period = @utc ? period : get_period_and_ensure_valid_local_time
-
end
-
-
# Returns a Time or DateTime instance that represents the time in +time_zone+.
-
1
def time
-
@time ||= period.to_local(@utc)
-
end
-
-
# Returns a Time or DateTime instance that represents the time in UTC.
-
1
def utc
-
@utc ||= period.to_utc(@time)
-
end
-
1
alias_method :comparable_time, :utc
-
1
alias_method :getgm, :utc
-
1
alias_method :getutc, :utc
-
1
alias_method :gmtime, :utc
-
-
# Returns the underlying TZInfo::TimezonePeriod.
-
1
def period
-
@period ||= time_zone.period_for_utc(@utc)
-
end
-
-
# Returns the simultaneous time in <tt>Time.zone</tt>, or the specified zone.
-
1
def in_time_zone(new_zone = ::Time.zone)
-
return self if time_zone == new_zone
-
utc.in_time_zone(new_zone)
-
end
-
-
# Returns a <tt>Time.local()</tt> instance of the simultaneous time in your system's <tt>ENV['TZ']</tt> zone
-
1
def localtime
-
utc.respond_to?(:getlocal) ? utc.getlocal : utc.to_time.getlocal
-
end
-
1
alias_method :getlocal, :localtime
-
-
1
def dst?
-
period.dst?
-
end
-
1
alias_method :isdst, :dst?
-
-
1
def utc?
-
time_zone.name == 'UTC'
-
end
-
1
alias_method :gmt?, :utc?
-
-
1
def utc_offset
-
period.utc_total_offset
-
end
-
1
alias_method :gmt_offset, :utc_offset
-
1
alias_method :gmtoff, :utc_offset
-
-
1
def formatted_offset(colon = true, alternate_utc_string = nil)
-
utc? && alternate_utc_string || TimeZone.seconds_to_utc_offset(utc_offset, colon)
-
end
-
-
# Time uses +zone+ to display the time zone abbreviation, so we're duck-typing it.
-
1
def zone
-
period.zone_identifier.to_s
-
end
-
-
1
def inspect
-
"#{time.strftime('%a, %d %b %Y %H:%M:%S')} #{zone} #{formatted_offset}"
-
end
-
-
1
def xmlschema(fraction_digits = 0)
-
fraction = if fraction_digits > 0
-
".%i" % time.usec.to_s[0, fraction_digits]
-
end
-
-
"#{time.strftime("%Y-%m-%dT%H:%M:%S")}#{fraction}#{formatted_offset(true, 'Z')}"
-
end
-
1
alias_method :iso8601, :xmlschema
-
-
# Coerces time to a string for JSON encoding. The default format is ISO 8601. You can get
-
# %Y/%m/%d %H:%M:%S +offset style by setting <tt>ActiveSupport::JSON::Encoding.use_standard_json_time_format</tt>
-
# to false.
-
#
-
# ==== Examples
-
#
-
# # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = true
-
# Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
-
# # => "2005-02-01T15:15:10Z"
-
#
-
# # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = false
-
# Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
-
# # => "2005/02/01 15:15:10 +0000"
-
#
-
1
def as_json(options = nil)
-
if ActiveSupport::JSON::Encoding.use_standard_json_time_format
-
xmlschema
-
else
-
%(#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
-
end
-
end
-
-
1
def encode_with(coder)
-
if coder.respond_to?(:represent_object)
-
coder.represent_object(nil, utc)
-
else
-
coder.represent_scalar(nil, utc.strftime("%Y-%m-%d %H:%M:%S.%9NZ"))
-
end
-
end
-
-
1
def to_yaml(options = {})
-
return super if defined?(YAML::ENGINE) && !YAML::ENGINE.syck?
-
-
utc.to_yaml(options)
-
end
-
-
1
def httpdate
-
utc.httpdate
-
end
-
-
1
def rfc2822
-
to_s(:rfc822)
-
end
-
1
alias_method :rfc822, :rfc2822
-
-
# <tt>:db</tt> format outputs time in UTC; all others output time in local.
-
# Uses TimeWithZone's +strftime+, so <tt>%Z</tt> and <tt>%z</tt> work correctly.
-
1
def to_s(format = :default)
-
if format == :db
-
utc.to_s(format)
-
elsif formatter = ::Time::DATE_FORMATS[format]
-
formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
-
else
-
"#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" # mimicking Ruby 1.9 Time#to_s format
-
end
-
end
-
1
alias_method :to_formatted_s, :to_s
-
-
# Replaces <tt>%Z</tt> and <tt>%z</tt> directives with +zone+ and +formatted_offset+, respectively, before passing to
-
# Time#strftime, so that zone information is correct
-
1
def strftime(format)
-
format = format.gsub('%Z', zone).gsub('%z', formatted_offset(false))
-
time.strftime(format)
-
end
-
-
# Use the time in UTC for comparisons.
-
1
def <=>(other)
-
utc <=> other
-
end
-
-
1
def between?(min, max)
-
utc.between?(min, max)
-
end
-
-
1
def past?
-
utc.past?
-
end
-
-
1
def today?
-
time.today?
-
end
-
-
1
def future?
-
utc.future?
-
end
-
-
1
def eql?(other)
-
utc == other
-
end
-
-
1
def +(other)
-
# If we're adding a Duration of variable length (i.e., years, months, days), move forward from #time,
-
# otherwise move forward from #utc, for accuracy when moving across DST boundaries
-
if duration_of_variable_length?(other)
-
method_missing(:+, other)
-
else
-
result = utc.acts_like?(:date) ? utc.since(other) : utc + other rescue utc.since(other)
-
result.in_time_zone(time_zone)
-
end
-
end
-
-
1
def -(other)
-
# If we're subtracting a Duration of variable length (i.e., years, months, days), move backwards from #time,
-
# otherwise move backwards #utc, for accuracy when moving across DST boundaries
-
if other.acts_like?(:time)
-
utc.to_f - other.to_f
-
elsif duration_of_variable_length?(other)
-
method_missing(:-, other)
-
else
-
result = utc.acts_like?(:date) ? utc.ago(other) : utc - other rescue utc.ago(other)
-
result.in_time_zone(time_zone)
-
end
-
end
-
-
1
def since(other)
-
# If we're adding a Duration of variable length (i.e., years, months, days), move forward from #time,
-
# otherwise move forward from #utc, for accuracy when moving across DST boundaries
-
if duration_of_variable_length?(other)
-
method_missing(:since, other)
-
else
-
utc.since(other).in_time_zone(time_zone)
-
end
-
end
-
-
1
def ago(other)
-
since(-other)
-
end
-
-
1
def advance(options)
-
# If we're advancing a value of variable length (i.e., years, weeks, months, days), advance from #time,
-
# otherwise advance from #utc, for accuracy when moving across DST boundaries
-
if options.values_at(:years, :weeks, :months, :days).any?
-
method_missing(:advance, options)
-
else
-
utc.advance(options).in_time_zone(time_zone)
-
end
-
end
-
-
1
%w(year mon month day mday wday yday hour min sec to_date).each do |method_name|
-
11
class_eval <<-EOV, __FILE__, __LINE__ + 1
-
def #{method_name} # def month
-
time.#{method_name} # time.month
-
end # end
-
EOV
-
end
-
-
1
def usec
-
time.respond_to?(:usec) ? time.usec : 0
-
end
-
-
1
def to_a
-
[time.sec, time.min, time.hour, time.day, time.mon, time.year, time.wday, time.yday, dst?, zone]
-
end
-
-
1
def to_f
-
utc.to_f
-
end
-
-
1
def to_i
-
utc.to_i
-
end
-
1
alias_method :hash, :to_i
-
1
alias_method :tv_sec, :to_i
-
-
# A TimeWithZone acts like a Time, so just return +self+.
-
1
def to_time
-
utc
-
end
-
-
1
def to_datetime
-
utc.to_datetime.new_offset(Rational(utc_offset, 86_400))
-
end
-
-
# So that +self+ <tt>acts_like?(:time)</tt>.
-
1
def acts_like_time?
-
true
-
end
-
-
# Say we're a Time to thwart type checking.
-
1
def is_a?(klass)
-
klass == ::Time || super
-
end
-
1
alias_method :kind_of?, :is_a?
-
-
1
def freeze
-
period; utc; time # preload instance variables before freezing
-
super
-
end
-
-
1
def marshal_dump
-
[utc, time_zone.name, time]
-
end
-
-
1
def marshal_load(variables)
-
initialize(variables[0].utc, ::Time.find_zone(variables[1]), variables[2].utc)
-
end
-
-
# Ensure proxy class responds to all methods that underlying time instance responds to.
-
1
def respond_to?(sym, include_priv = false)
-
# consistently respond false to acts_like?(:date), regardless of whether #time is a Time or DateTime
-
return false if sym.to_s == 'acts_like_date?'
-
super || time.respond_to?(sym, include_priv)
-
end
-
-
# Send the missing method to +time+ instance, and wrap result in a new TimeWithZone with the existing +time_zone+.
-
1
def method_missing(sym, *args, &block)
-
result = time.__send__(sym, *args, &block)
-
result.acts_like?(:time) ? self.class.new(nil, time_zone, result) : result
-
end
-
-
1
private
-
1
def get_period_and_ensure_valid_local_time
-
# we don't want a Time.local instance enforcing its own DST rules as well,
-
# so transfer time values to a utc constructor if necessary
-
@time = transfer_time_values_to_utc_constructor(@time) unless @time.utc?
-
begin
-
@time_zone.period_for_local(@time)
-
rescue ::TZInfo::PeriodNotFound
-
# time is in the "spring forward" hour gap, so we're moving the time forward one hour and trying again
-
@time += 1.hour
-
retry
-
end
-
end
-
-
1
def transfer_time_values_to_utc_constructor(time)
-
::Time.utc_time(time.year, time.month, time.day, time.hour, time.min, time.sec, time.respond_to?(:usec) ? time.usec : 0)
-
end
-
-
1
def duration_of_variable_length?(obj)
-
ActiveSupport::Duration === obj && obj.parts.any? {|p| p[0].in?([:years, :months, :days]) }
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/object/try'
-
-
# The TimeZone class serves as a wrapper around TZInfo::Timezone instances. It allows us to do the following:
-
#
-
# * Limit the set of zones provided by TZInfo to a meaningful subset of 142 zones.
-
# * Retrieve and display zones with a friendlier name (e.g., "Eastern Time (US & Canada)" instead of "America/New_York").
-
# * Lazily load TZInfo::Timezone instances only when they're needed.
-
# * Create ActiveSupport::TimeWithZone instances via TimeZone's +local+, +parse+, +at+ and +now+ methods.
-
#
-
# If you set <tt>config.time_zone</tt> in the Rails Application, you can access this TimeZone object via <tt>Time.zone</tt>:
-
#
-
# # application.rb:
-
# class Application < Rails::Application
-
# config.time_zone = "Eastern Time (US & Canada)"
-
# end
-
#
-
# Time.zone # => #<TimeZone:0x514834...>
-
# Time.zone.name # => "Eastern Time (US & Canada)"
-
# Time.zone.now # => Sun, 18 May 2008 14:30:44 EDT -04:00
-
#
-
# The version of TZInfo bundled with Active Support only includes the definitions necessary to support the zones
-
# defined by the TimeZone class. If you need to use zones that aren't defined by TimeZone, you'll need to install the TZInfo gem
-
# (if a recent version of the gem is installed locally, this will be used instead of the bundled version.)
-
1
module ActiveSupport
-
1
class TimeZone
-
# Keys are Rails TimeZone names, values are TZInfo identifiers
-
1
MAPPING = {
-
"International Date Line West" => "Pacific/Midway",
-
"Midway Island" => "Pacific/Midway",
-
"Samoa" => "Pacific/Pago_Pago",
-
"Hawaii" => "Pacific/Honolulu",
-
"Alaska" => "America/Juneau",
-
"Pacific Time (US & Canada)" => "America/Los_Angeles",
-
"Tijuana" => "America/Tijuana",
-
"Mountain Time (US & Canada)" => "America/Denver",
-
"Arizona" => "America/Phoenix",
-
"Chihuahua" => "America/Chihuahua",
-
"Mazatlan" => "America/Mazatlan",
-
"Central Time (US & Canada)" => "America/Chicago",
-
"Saskatchewan" => "America/Regina",
-
"Guadalajara" => "America/Mexico_City",
-
"Mexico City" => "America/Mexico_City",
-
"Monterrey" => "America/Monterrey",
-
"Central America" => "America/Guatemala",
-
"Eastern Time (US & Canada)" => "America/New_York",
-
"Indiana (East)" => "America/Indiana/Indianapolis",
-
"Bogota" => "America/Bogota",
-
"Lima" => "America/Lima",
-
"Quito" => "America/Lima",
-
"Atlantic Time (Canada)" => "America/Halifax",
-
"Caracas" => "America/Caracas",
-
"La Paz" => "America/La_Paz",
-
"Santiago" => "America/Santiago",
-
"Newfoundland" => "America/St_Johns",
-
"Brasilia" => "America/Sao_Paulo",
-
"Buenos Aires" => "America/Argentina/Buenos_Aires",
-
"Georgetown" => "America/Guyana",
-
"Greenland" => "America/Godthab",
-
"Mid-Atlantic" => "Atlantic/South_Georgia",
-
"Azores" => "Atlantic/Azores",
-
"Cape Verde Is." => "Atlantic/Cape_Verde",
-
"Dublin" => "Europe/Dublin",
-
"Edinburgh" => "Europe/London",
-
"Lisbon" => "Europe/Lisbon",
-
"London" => "Europe/London",
-
"Casablanca" => "Africa/Casablanca",
-
"Monrovia" => "Africa/Monrovia",
-
"UTC" => "Etc/UTC",
-
"Belgrade" => "Europe/Belgrade",
-
"Bratislava" => "Europe/Bratislava",
-
"Budapest" => "Europe/Budapest",
-
"Ljubljana" => "Europe/Ljubljana",
-
"Prague" => "Europe/Prague",
-
"Sarajevo" => "Europe/Sarajevo",
-
"Skopje" => "Europe/Skopje",
-
"Warsaw" => "Europe/Warsaw",
-
"Zagreb" => "Europe/Zagreb",
-
"Brussels" => "Europe/Brussels",
-
"Copenhagen" => "Europe/Copenhagen",
-
"Madrid" => "Europe/Madrid",
-
"Paris" => "Europe/Paris",
-
"Amsterdam" => "Europe/Amsterdam",
-
"Berlin" => "Europe/Berlin",
-
"Bern" => "Europe/Berlin",
-
"Rome" => "Europe/Rome",
-
"Stockholm" => "Europe/Stockholm",
-
"Vienna" => "Europe/Vienna",
-
"West Central Africa" => "Africa/Algiers",
-
"Bucharest" => "Europe/Bucharest",
-
"Cairo" => "Africa/Cairo",
-
"Helsinki" => "Europe/Helsinki",
-
"Kyiv" => "Europe/Kiev",
-
"Riga" => "Europe/Riga",
-
"Sofia" => "Europe/Sofia",
-
"Tallinn" => "Europe/Tallinn",
-
"Vilnius" => "Europe/Vilnius",
-
"Athens" => "Europe/Athens",
-
"Istanbul" => "Europe/Istanbul",
-
"Minsk" => "Europe/Minsk",
-
"Jerusalem" => "Asia/Jerusalem",
-
"Harare" => "Africa/Harare",
-
"Pretoria" => "Africa/Johannesburg",
-
"Moscow" => "Europe/Moscow",
-
"St. Petersburg" => "Europe/Moscow",
-
"Volgograd" => "Europe/Moscow",
-
"Kuwait" => "Asia/Kuwait",
-
"Riyadh" => "Asia/Riyadh",
-
"Nairobi" => "Africa/Nairobi",
-
"Baghdad" => "Asia/Baghdad",
-
"Tehran" => "Asia/Tehran",
-
"Abu Dhabi" => "Asia/Muscat",
-
"Muscat" => "Asia/Muscat",
-
"Baku" => "Asia/Baku",
-
"Tbilisi" => "Asia/Tbilisi",
-
"Yerevan" => "Asia/Yerevan",
-
"Kabul" => "Asia/Kabul",
-
"Ekaterinburg" => "Asia/Yekaterinburg",
-
"Islamabad" => "Asia/Karachi",
-
"Karachi" => "Asia/Karachi",
-
"Tashkent" => "Asia/Tashkent",
-
"Chennai" => "Asia/Kolkata",
-
"Kolkata" => "Asia/Kolkata",
-
"Mumbai" => "Asia/Kolkata",
-
"New Delhi" => "Asia/Kolkata",
-
"Kathmandu" => "Asia/Kathmandu",
-
"Astana" => "Asia/Dhaka",
-
"Dhaka" => "Asia/Dhaka",
-
"Sri Jayawardenepura" => "Asia/Colombo",
-
"Almaty" => "Asia/Almaty",
-
"Novosibirsk" => "Asia/Novosibirsk",
-
"Rangoon" => "Asia/Rangoon",
-
"Bangkok" => "Asia/Bangkok",
-
"Hanoi" => "Asia/Bangkok",
-
"Jakarta" => "Asia/Jakarta",
-
"Krasnoyarsk" => "Asia/Krasnoyarsk",
-
"Beijing" => "Asia/Shanghai",
-
"Chongqing" => "Asia/Chongqing",
-
"Hong Kong" => "Asia/Hong_Kong",
-
"Urumqi" => "Asia/Urumqi",
-
"Kuala Lumpur" => "Asia/Kuala_Lumpur",
-
"Singapore" => "Asia/Singapore",
-
"Taipei" => "Asia/Taipei",
-
"Perth" => "Australia/Perth",
-
"Irkutsk" => "Asia/Irkutsk",
-
"Ulaan Bataar" => "Asia/Ulaanbaatar",
-
"Seoul" => "Asia/Seoul",
-
"Osaka" => "Asia/Tokyo",
-
"Sapporo" => "Asia/Tokyo",
-
"Tokyo" => "Asia/Tokyo",
-
"Yakutsk" => "Asia/Yakutsk",
-
"Darwin" => "Australia/Darwin",
-
"Adelaide" => "Australia/Adelaide",
-
"Canberra" => "Australia/Melbourne",
-
"Melbourne" => "Australia/Melbourne",
-
"Sydney" => "Australia/Sydney",
-
"Brisbane" => "Australia/Brisbane",
-
"Hobart" => "Australia/Hobart",
-
"Vladivostok" => "Asia/Vladivostok",
-
"Guam" => "Pacific/Guam",
-
"Port Moresby" => "Pacific/Port_Moresby",
-
"Magadan" => "Asia/Magadan",
-
"Solomon Is." => "Asia/Magadan",
-
"New Caledonia" => "Pacific/Noumea",
-
"Fiji" => "Pacific/Fiji",
-
"Kamchatka" => "Asia/Kamchatka",
-
"Marshall Is." => "Pacific/Majuro",
-
"Auckland" => "Pacific/Auckland",
-
"Wellington" => "Pacific/Auckland",
-
"Nuku'alofa" => "Pacific/Tongatapu"
-
142
}.each { |name, zone| name.freeze; zone.freeze }
-
1
MAPPING.freeze
-
-
1
UTC_OFFSET_WITH_COLON = '%s%02d:%02d'
-
1
UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.sub(':', '')
-
-
# Assumes self represents an offset from UTC in seconds (as returned from Time#utc_offset)
-
# and turns this into an +HH:MM formatted string. Example:
-
#
-
# TimeZone.seconds_to_utc_offset(-21_600) # => "-06:00"
-
1
def self.seconds_to_utc_offset(seconds, colon = true)
-
format = colon ? UTC_OFFSET_WITH_COLON : UTC_OFFSET_WITHOUT_COLON
-
sign = (seconds < 0 ? '-' : '+')
-
hours = seconds.abs / 3600
-
minutes = (seconds.abs % 3600) / 60
-
format % [sign, hours, minutes]
-
end
-
-
1
include Comparable
-
1
attr_reader :name
-
1
attr_reader :tzinfo
-
-
# Create a new TimeZone object with the given name and offset. The
-
# offset is the number of seconds that this time zone is offset from UTC
-
# (GMT). Seconds were chosen as the offset unit because that is the unit that
-
# Ruby uses to represent time zone offsets (see Time#utc_offset).
-
1
def initialize(name, utc_offset = nil, tzinfo = nil)
-
1
self.class.send(:require_tzinfo)
-
-
1
@name = name
-
1
@utc_offset = utc_offset
-
1
@tzinfo = tzinfo || TimeZone.find_tzinfo(name)
-
1
@current_period = nil
-
end
-
-
1
def utc_offset
-
if @utc_offset
-
@utc_offset
-
else
-
@current_period ||= tzinfo.try(:current_period)
-
@current_period.try(:utc_offset)
-
end
-
end
-
-
# Returns the offset of this time zone as a formatted string, of the
-
# format "+HH:MM".
-
1
def formatted_offset(colon=true, alternate_utc_string = nil)
-
utc_offset == 0 && alternate_utc_string || self.class.seconds_to_utc_offset(utc_offset, colon)
-
end
-
-
# Compare this time zone to the parameter. The two are compared first on
-
# their offsets, and then by name.
-
1
def <=>(zone)
-
result = (utc_offset <=> zone.utc_offset)
-
result = (name <=> zone.name) if result == 0
-
result
-
end
-
-
# Compare #name and TZInfo identifier to a supplied regexp, returning true
-
# if a match is found.
-
1
def =~(re)
-
return true if name =~ re || MAPPING[name] =~ re
-
end
-
-
# Returns a textual representation of this time zone.
-
1
def to_s
-
"(GMT#{formatted_offset}) #{name}"
-
end
-
-
# Method for creating new ActiveSupport::TimeWithZone instance in time zone of +self+ from given values. Example:
-
#
-
# Time.zone = "Hawaii" # => "Hawaii"
-
# Time.zone.local(2007, 2, 1, 15, 30, 45) # => Thu, 01 Feb 2007 15:30:45 HST -10:00
-
1
def local(*args)
-
time = Time.utc_time(*args)
-
ActiveSupport::TimeWithZone.new(nil, self, time)
-
end
-
-
# Method for creating new ActiveSupport::TimeWithZone instance in time zone of +self+ from number of seconds since the Unix epoch. Example:
-
#
-
# Time.zone = "Hawaii" # => "Hawaii"
-
# Time.utc(2000).to_f # => 946684800.0
-
# Time.zone.at(946684800.0) # => Fri, 31 Dec 1999 14:00:00 HST -10:00
-
1
def at(secs)
-
utc = Time.at(secs).utc rescue DateTime.civil(1970).since(secs)
-
utc.in_time_zone(self)
-
end
-
-
# Method for creating new ActiveSupport::TimeWithZone instance in time zone of +self+ from parsed string. Example:
-
#
-
# Time.zone = "Hawaii" # => "Hawaii"
-
# Time.zone.parse('1999-12-31 14:00:00') # => Fri, 31 Dec 1999 14:00:00 HST -10:00
-
#
-
# If upper components are missing from the string, they are supplied from TimeZone#now:
-
#
-
# Time.zone.now # => Fri, 31 Dec 1999 14:00:00 HST -10:00
-
# Time.zone.parse('22:30:00') # => Fri, 31 Dec 1999 22:30:00 HST -10:00
-
1
def parse(str, now=now)
-
date_parts = Date._parse(str)
-
return if date_parts.blank?
-
time = Time.parse(str, now) rescue DateTime.parse(str)
-
if date_parts[:offset].nil?
-
ActiveSupport::TimeWithZone.new(nil, self, time)
-
else
-
time.in_time_zone(self)
-
end
-
end
-
-
# Returns an ActiveSupport::TimeWithZone instance representing the current time
-
# in the time zone represented by +self+. Example:
-
#
-
# Time.zone = 'Hawaii' # => "Hawaii"
-
# Time.zone.now # => Wed, 23 Jan 2008 20:24:27 HST -10:00
-
1
def now
-
Time.now.utc.in_time_zone(self)
-
end
-
-
# Return the current date in this time zone.
-
1
def today
-
tzinfo.now.to_date
-
end
-
-
# Adjust the given time to the simultaneous time in the time zone represented by +self+. Returns a
-
# Time.utc() instance -- if you want an ActiveSupport::TimeWithZone instance, use Time#in_time_zone() instead.
-
1
def utc_to_local(time)
-
tzinfo.utc_to_local(time)
-
end
-
-
# Adjust the given time to the simultaneous time in UTC. Returns a Time.utc() instance.
-
1
def local_to_utc(time, dst=true)
-
tzinfo.local_to_utc(time, dst)
-
end
-
-
# Available so that TimeZone instances respond like TZInfo::Timezone instances
-
1
def period_for_utc(time)
-
tzinfo.period_for_utc(time)
-
end
-
-
# Available so that TimeZone instances respond like TZInfo::Timezone instances
-
1
def period_for_local(time, dst=true)
-
tzinfo.period_for_local(time, dst)
-
end
-
-
# TODO: Preload instead of lazy load for thread safety
-
1
def self.find_tzinfo(name)
-
1
require 'active_support/tzinfo' unless defined?(::TZInfo)
-
1
::TZInfo::TimezoneProxy.new(MAPPING[name] || name)
-
end
-
-
1
class << self
-
1
alias_method :create, :new
-
-
# Return a TimeZone instance with the given name, or +nil+ if no
-
# such TimeZone instance exists. (This exists to support the use of
-
# this class with the +composed_of+ macro.)
-
1
def new(name)
-
self[name]
-
end
-
-
# Return an array of all TimeZone objects. There are multiple
-
# TimeZone objects per time zone, in many cases, to make it easier
-
# for users to find their own time zone.
-
1
def all
-
@zones ||= zones_map.values.sort
-
end
-
-
1
def zones_map
-
@zones_map ||= begin
-
new_zones_names = MAPPING.keys - lazy_zones_map.keys
-
new_zones = Hash[new_zones_names.map { |place| [place, create(place)] }]
-
-
lazy_zones_map.merge(new_zones)
-
end
-
end
-
-
# Locate a specific time zone object. If the argument is a string, it
-
# is interpreted to mean the name of the timezone to locate. If it is a
-
# numeric value it is either the hour offset, or the second offset, of the
-
# timezone to find. (The first one with that offset will be returned.)
-
# Returns +nil+ if no such time zone is known to the system.
-
1
def [](arg)
-
1
case arg
-
when String
-
1
begin
-
1
lazy_zones_map[arg] ||= lookup(arg).tap { |tz| tz.utc_offset }
-
rescue TZInfo::InvalidTimezoneIdentifier
-
nil
-
end
-
when Numeric, ActiveSupport::Duration
-
arg *= 3600 if arg.abs <= 13
-
all.find { |z| z.utc_offset == arg.to_i }
-
else
-
raise ArgumentError, "invalid argument to TimeZone[]: #{arg.inspect}"
-
end
-
end
-
-
# A convenience method for returning a collection of TimeZone objects
-
# for time zones in the USA.
-
1
def us_zones
-
@us_zones ||= all.find_all { |z| z.name =~ /US|Arizona|Indiana|Hawaii|Alaska/ }
-
end
-
-
1
protected
-
-
1
def require_tzinfo
-
2
require 'tzinfo'
-
rescue LoadError
-
$stderr.puts "You don't have tzinfo installed in your application. Please add it to your Gemfile and run bundle install"
-
raise
-
end
-
-
1
private
-
-
1
def lookup(name)
-
(tzinfo = find_tzinfo(name)) && create(tzinfo.name.freeze)
-
end
-
-
1
def lazy_zones_map
-
1
require_tzinfo
-
-
@lazy_zones_map ||= Hash.new do |hash, place|
-
1
hash[place] = create(place) if MAPPING.has_key?(place)
-
1
end
-
end
-
end
-
end
-
end
-
# Extensions to +nil+ which allow for more helpful error messages for people who
-
# are new to Rails.
-
#
-
# Ruby raises NoMethodError if you invoke a method on an object that does not
-
# respond to it:
-
#
-
# $ ruby -e nil.destroy
-
# -e:1: undefined method `destroy' for nil:NilClass (NoMethodError)
-
#
-
# With these extensions, if the method belongs to the public interface of the
-
# classes in NilClass::WHINERS the error message suggests which could be the
-
# actual intended class:
-
#
-
# $ rails runner nil.destroy
-
# ...
-
# You might have expected an instance of ActiveRecord::Base.
-
# ...
-
#
-
# NilClass#id exists in Ruby 1.8 (though it is deprecated). Since +id+ is a fundamental
-
# method of Active Record models NilClass#id is redefined as well to raise a RuntimeError
-
# and warn the user. She probably wanted a model database identifier and the 4
-
# returned by the original method could result in obscure bugs.
-
#
-
# The flag <tt>config.whiny_nils</tt> determines whether this feature is enabled.
-
# By default it is on in development and test modes, and it is off in production
-
# mode.
-
1
class NilClass
-
1
METHOD_CLASS_MAP = Hash.new
-
-
1
def self.add_whiner(klass)
-
2
methods = klass.public_instance_methods - public_instance_methods
-
2
class_name = klass.name
-
311
methods.each { |method| METHOD_CLASS_MAP[method.to_sym] = class_name }
-
end
-
-
1
add_whiner ::Array
-
-
# Raises a RuntimeError when you attempt to call +id+ on +nil+.
-
1
def id
-
raise RuntimeError, "Called id for nil, which would mistakenly be #{object_id} -- if you really wanted the id of nil, use object_id", caller
-
end
-
-
1
private
-
1
def method_missing(method, *args)
-
42
if klass = METHOD_CLASS_MAP[method]
-
raise_nil_warning_for klass, method, caller
-
else
-
42
super
-
end
-
end
-
-
# Raises a NoMethodError when you attempt to call a method on +nil+.
-
1
def raise_nil_warning_for(class_name = nil, selector = nil, with_caller = nil)
-
message = "You have a nil object when you didn't expect it!"
-
message << "\nYou might have expected an instance of #{class_name}." if class_name
-
message << "\nThe error occurred while evaluating nil.#{selector}" if selector
-
-
raise NoMethodError, message, with_caller || caller
-
end
-
end
-
# encoding:utf-8
-
#--
-
# Copyright (C) 2006-2011 Bob Aman
-
#
-
# Licensed under the Apache License, Version 2.0 (the "License");
-
# you may not use this file except in compliance with the License.
-
# You may obtain a copy of the License at
-
#
-
# http://www.apache.org/licenses/LICENSE-2.0
-
#
-
# Unless required by applicable law or agreed to in writing, software
-
# distributed under the License is distributed on an "AS IS" BASIS,
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-
# See the License for the specific language governing permissions and
-
# limitations under the License.
-
#++
-
-
-
1
begin
-
1
require "addressable/idna/native"
-
rescue LoadError
-
# libidn or the idn gem was not available, fall back on a pure-Ruby
-
# implementation...
-
1
require "addressable/idna/pure"
-
end
-
# encoding:utf-8
-
#--
-
# Copyright (C) 2006-2011 Bob Aman
-
#
-
# Licensed under the Apache License, Version 2.0 (the "License");
-
# you may not use this file except in compliance with the License.
-
# You may obtain a copy of the License at
-
#
-
# http://www.apache.org/licenses/LICENSE-2.0
-
#
-
# Unless required by applicable law or agreed to in writing, software
-
# distributed under the License is distributed on an "AS IS" BASIS,
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-
# See the License for the specific language governing permissions and
-
# limitations under the License.
-
#++
-
-
-
1
require "idn"
-
-
module Addressable
-
module IDNA
-
def self.punycode_encode(value)
-
IDN::Punycode.encode(value)
-
end
-
-
def self.punycode_decode(value)
-
IDN::Punycode.decode(value)
-
end
-
-
def self.unicode_normalize_kc(value)
-
IDN::Stringprep.nfkc_normalize(value)
-
end
-
-
def self.to_ascii(value)
-
IDN::Idna.toASCII(value)
-
end
-
-
def self.to_unicode(value)
-
IDN::Idna.toUnicode(value)
-
end
-
end
-
end
-
# encoding:utf-8
-
#--
-
# Copyright (C) 2006-2011 Bob Aman
-
#
-
# Licensed under the Apache License, Version 2.0 (the "License");
-
# you may not use this file except in compliance with the License.
-
# You may obtain a copy of the License at
-
#
-
# http://www.apache.org/licenses/LICENSE-2.0
-
#
-
# Unless required by applicable law or agreed to in writing, software
-
# distributed under the License is distributed on an "AS IS" BASIS,
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-
# See the License for the specific language governing permissions and
-
# limitations under the License.
-
#++
-
-
-
1
module Addressable
-
1
module IDNA
-
# This module is loosely based on idn_actionmailer by Mick Staugaard,
-
# the unicode library by Yoshida Masato, and the punycode implementation
-
# by Kazuhiro Nishiyama. Most of the code was copied verbatim, but
-
# some reformatting was done, and some translation from C was done.
-
#
-
# Without their code to work from as a base, we'd all still be relying
-
# on the presence of libidn. Which nobody ever seems to have installed.
-
#
-
# Original sources:
-
# http://github.com/staugaard/idn_actionmailer
-
# http://www.yoshidam.net/Ruby.html#unicode
-
# http://rubyforge.org/frs/?group_id=2550
-
-
-
1
ACE_PREFIX = "xn--"
-
-
1
UTF8_REGEX = /\A(?:
-
[\x09\x0A\x0D\x20-\x7E] # ASCII
-
| [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
-
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
-
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
-
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
-
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
-
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4nil5
-
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
-
)*\z/mnx
-
-
1
UTF8_REGEX_MULTIBYTE = /(?:
-
[\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
-
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
-
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
-
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
-
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
-
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4nil5
-
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
-
)/mnx
-
-
# :startdoc:
-
-
# Converts from a Unicode internationalized domain name to an ASCII
-
# domain name as described in RFC 3490.
-
1
def self.to_ascii(input)
-
input = input.dup
-
if input.respond_to?(:force_encoding)
-
input.force_encoding(Encoding::ASCII_8BIT)
-
end
-
if input =~ UTF8_REGEX && input =~ UTF8_REGEX_MULTIBYTE
-
parts = unicode_downcase(input).split('.')
-
parts.map! do |part|
-
if part.respond_to?(:force_encoding)
-
part.force_encoding(Encoding::ASCII_8BIT)
-
end
-
if part =~ UTF8_REGEX && part =~ UTF8_REGEX_MULTIBYTE
-
ACE_PREFIX + punycode_encode(unicode_normalize_kc(part))
-
else
-
part
-
end
-
end
-
parts.join('.')
-
else
-
input
-
end
-
end
-
-
# Converts from an ASCII domain name to a Unicode internationalized
-
# domain name as described in RFC 3490.
-
1
def self.to_unicode(input)
-
parts = input.split('.')
-
parts.map! do |part|
-
if part =~ /^#{ACE_PREFIX}/
-
punycode_decode(part[/^#{ACE_PREFIX}(.+)/, 1])
-
else
-
part
-
end
-
end
-
output = parts.join('.')
-
if output.respond_to?(:force_encoding)
-
output.force_encoding(Encoding::UTF_8)
-
end
-
output
-
end
-
-
# Unicode normalization form KC.
-
1
def self.unicode_normalize_kc(input)
-
unpacked = input.unpack("U*")
-
unpacked =
-
unicode_compose(unicode_sort_canonical(unicode_decompose(unpacked)))
-
return unpacked.pack("U*")
-
end
-
-
##
-
# Unicode aware downcase method.
-
#
-
# @api private
-
# @param [String] input
-
# The input string.
-
# @return [String] The downcased result.
-
1
def self.unicode_downcase(input)
-
unpacked = input.unpack("U*")
-
unpacked.map! { |codepoint| lookup_unicode_lowercase(codepoint) }
-
return unpacked.pack("U*")
-
end
-
2
(class <<self; private :unicode_downcase; end)
-
-
1
def self.unicode_compose(unpacked)
-
unpacked_result = []
-
length = unpacked.length
-
-
return unpacked if length == 0
-
-
starter = unpacked[0]
-
starter_cc = lookup_unicode_combining_class(starter)
-
starter_cc = 256 if starter_cc != 0
-
for i in 1...length
-
ch = unpacked[i]
-
cc = lookup_unicode_combining_class(ch)
-
-
if (starter_cc == 0 &&
-
(composite = unicode_compose_pair(starter, ch)) != nil)
-
starter = composite
-
startercc = lookup_unicode_combining_class(composite)
-
else
-
unpacked_result << starter
-
starter = ch
-
startercc = cc
-
end
-
end
-
unpacked_result << starter
-
return unpacked_result
-
end
-
2
(class <<self; private :unicode_compose; end)
-
-
1
def self.unicode_compose_pair(ch_one, ch_two)
-
if ch_one >= HANGUL_LBASE && ch_one < HANGUL_LBASE + HANGUL_LCOUNT &&
-
ch_two >= HANGUL_VBASE && ch_two < HANGUL_VBASE + HANGUL_VCOUNT
-
# Hangul L + V
-
return HANGUL_SBASE + (
-
(ch_one - HANGUL_LBASE) * HANGUL_VCOUNT + (ch_two - HANGUL_VBASE)
-
) * HANGUL_TCOUNT
-
elsif ch_one >= HANGUL_SBASE &&
-
ch_one < HANGUL_SBASE + HANGUL_SCOUNT &&
-
(ch_one - HANGUL_SBASE) % HANGUL_TCOUNT == 0 &&
-
ch_two >= HANGUL_TBASE && ch_two < HANGUL_TBASE + HANGUL_TCOUNT
-
# Hangul LV + T
-
return ch_one + (ch_two - HANGUL_TBASE)
-
end
-
-
p = []
-
ucs4_to_utf8 = lambda do |ch|
-
# For some reason, rcov likes to drop BUS errors here.
-
if ch < 128
-
p << ch
-
elsif ch < 2048
-
p << (ch >> 6 | 192)
-
p << (ch & 63 | 128)
-
elsif ch < 0x10000
-
p << (ch >> 12 | 224)
-
p << (ch >> 6 & 63 | 128)
-
p << (ch & 63 | 128)
-
elsif ch < 0x200000
-
p << (ch >> 18 | 240)
-
p << (ch >> 12 & 63 | 128)
-
p << (ch >> 6 & 63 | 128)
-
p << (ch & 63 | 128)
-
elsif ch < 0x4000000
-
p << (ch >> 24 | 248)
-
p << (ch >> 18 & 63 | 128)
-
p << (ch >> 12 & 63 | 128)
-
p << (ch >> 6 & 63 | 128)
-
p << (ch & 63 | 128)
-
elsif ch < 0x80000000
-
p << (ch >> 30 | 252)
-
p << (ch >> 24 & 63 | 128)
-
p << (ch >> 18 & 63 | 128)
-
p << (ch >> 12 & 63 | 128)
-
p << (ch >> 6 & 63 | 128)
-
p << (ch & 63 | 128)
-
end
-
end
-
-
ucs4_to_utf8.call(ch_one)
-
ucs4_to_utf8.call(ch_two)
-
-
return lookup_unicode_composition(p)
-
end
-
2
(class <<self; private :unicode_compose_pair; end)
-
-
1
def self.unicode_sort_canonical(unpacked)
-
unpacked = unpacked.dup
-
i = 1
-
length = unpacked.length
-
-
return unpacked if length < 2
-
-
while i < length
-
last = unpacked[i-1]
-
ch = unpacked[i]
-
last_cc = lookup_unicode_combining_class(last)
-
cc = lookup_unicode_combining_class(ch)
-
if cc != 0 && last_cc != 0 && last_cc > cc
-
unpacked[i] = last
-
unpacked[i-1] = ch
-
i -= 1 if i > 1
-
else
-
i += 1
-
end
-
end
-
return unpacked
-
end
-
2
(class <<self; private :unicode_sort_canonical; end)
-
-
1
def self.unicode_decompose(unpacked)
-
unpacked_result = []
-
for cp in unpacked
-
if cp >= HANGUL_SBASE && cp < HANGUL_SBASE + HANGUL_SCOUNT
-
l, v, t = unicode_decompose_hangul(cp)
-
unpacked_result << l
-
unpacked_result << v if v
-
unpacked_result << t if t
-
else
-
dc = lookup_unicode_compatibility(cp)
-
unless dc
-
unpacked_result << cp
-
else
-
unpacked_result.concat(unicode_decompose(dc.unpack("U*")))
-
end
-
end
-
end
-
return unpacked_result
-
end
-
2
(class <<self; private :unicode_decompose; end)
-
-
1
def self.unicode_decompose_hangul(codepoint)
-
sindex = codepoint - HANGUL_SBASE;
-
if sindex < 0 || sindex >= HANGUL_SCOUNT
-
l = codepoint
-
v = t = nil
-
return l, v, t
-
end
-
l = HANGUL_LBASE + sindex / HANGUL_NCOUNT
-
v = HANGUL_VBASE + (sindex % HANGUL_NCOUNT) / HANGUL_TCOUNT
-
t = HANGUL_TBASE + sindex % HANGUL_TCOUNT
-
if t == HANGUL_TBASE
-
t = nil
-
end
-
return l, v, t
-
end
-
2
(class <<self; private :unicode_decompose_hangul; end)
-
-
1
def self.lookup_unicode_combining_class(codepoint)
-
codepoint_data = UNICODE_DATA[codepoint]
-
(codepoint_data ?
-
(codepoint_data[UNICODE_DATA_COMBINING_CLASS] || 0) :
-
0)
-
end
-
2
(class <<self; private :lookup_unicode_combining_class; end)
-
-
1
def self.lookup_unicode_compatibility(codepoint)
-
codepoint_data = UNICODE_DATA[codepoint]
-
(codepoint_data ?
-
codepoint_data[UNICODE_DATA_COMPATIBILITY] : nil)
-
end
-
2
(class <<self; private :lookup_unicode_compatibility; end)
-
-
1
def self.lookup_unicode_lowercase(codepoint)
-
codepoint_data = UNICODE_DATA[codepoint]
-
(codepoint_data ?
-
(codepoint_data[UNICODE_DATA_LOWERCASE] || codepoint) :
-
codepoint)
-
end
-
2
(class <<self; private :lookup_unicode_lowercase; end)
-
-
1
def self.lookup_unicode_composition(unpacked)
-
return COMPOSITION_TABLE[unpacked]
-
end
-
2
(class <<self; private :lookup_unicode_composition; end)
-
-
1
HANGUL_SBASE = 0xac00
-
1
HANGUL_LBASE = 0x1100
-
1
HANGUL_LCOUNT = 19
-
1
HANGUL_VBASE = 0x1161
-
1
HANGUL_VCOUNT = 21
-
1
HANGUL_TBASE = 0x11a7
-
1
HANGUL_TCOUNT = 28
-
1
HANGUL_NCOUNT = HANGUL_VCOUNT * HANGUL_TCOUNT # 588
-
1
HANGUL_SCOUNT = HANGUL_LCOUNT * HANGUL_NCOUNT # 11172
-
-
1
UNICODE_DATA_COMBINING_CLASS = 0
-
1
UNICODE_DATA_EXCLUSION = 1
-
1
UNICODE_DATA_CANONICAL = 2
-
1
UNICODE_DATA_COMPATIBILITY = 3
-
1
UNICODE_DATA_UPPERCASE = 4
-
1
UNICODE_DATA_LOWERCASE = 5
-
1
UNICODE_DATA_TITLECASE = 6
-
-
# This is a sparse Unicode table. Codepoints without entries are
-
# assumed to have the value: [0, 0, nil, nil, nil, nil, nil]
-
1
UNICODE_DATA = {
-
0x0041 => [0, 0, nil, nil, nil, 0x0061, nil],
-
0x0042 => [0, 0, nil, nil, nil, 0x0062, nil],
-
0x0043 => [0, 0, nil, nil, nil, 0x0063, nil],
-
0x0044 => [0, 0, nil, nil, nil, 0x0064, nil],
-
0x0045 => [0, 0, nil, nil, nil, 0x0065, nil],
-
0x0046 => [0, 0, nil, nil, nil, 0x0066, nil],
-
0x0047 => [0, 0, nil, nil, nil, 0x0067, nil],
-
0x0048 => [0, 0, nil, nil, nil, 0x0068, nil],
-
0x0049 => [0, 0, nil, nil, nil, 0x0069, nil],
-
0x004a => [0, 0, nil, nil, nil, 0x006a, nil],
-
0x004b => [0, 0, nil, nil, nil, 0x006b, nil],
-
0x004c => [0, 0, nil, nil, nil, 0x006c, nil],
-
0x004d => [0, 0, nil, nil, nil, 0x006d, nil],
-
0x004e => [0, 0, nil, nil, nil, 0x006e, nil],
-
0x004f => [0, 0, nil, nil, nil, 0x006f, nil],
-
0x0050 => [0, 0, nil, nil, nil, 0x0070, nil],
-
0x0051 => [0, 0, nil, nil, nil, 0x0071, nil],
-
0x0052 => [0, 0, nil, nil, nil, 0x0072, nil],
-
0x0053 => [0, 0, nil, nil, nil, 0x0073, nil],
-
0x0054 => [0, 0, nil, nil, nil, 0x0074, nil],
-
0x0055 => [0, 0, nil, nil, nil, 0x0075, nil],
-
0x0056 => [0, 0, nil, nil, nil, 0x0076, nil],
-
0x0057 => [0, 0, nil, nil, nil, 0x0077, nil],
-
0x0058 => [0, 0, nil, nil, nil, 0x0078, nil],
-
0x0059 => [0, 0, nil, nil, nil, 0x0079, nil],
-
0x005a => [0, 0, nil, nil, nil, 0x007a, nil],
-
0x0061 => [0, 0, nil, nil, 0x0041, nil, 0x0041],
-
0x0062 => [0, 0, nil, nil, 0x0042, nil, 0x0042],
-
0x0063 => [0, 0, nil, nil, 0x0043, nil, 0x0043],
-
0x0064 => [0, 0, nil, nil, 0x0044, nil, 0x0044],
-
0x0065 => [0, 0, nil, nil, 0x0045, nil, 0x0045],
-
0x0066 => [0, 0, nil, nil, 0x0046, nil, 0x0046],
-
0x0067 => [0, 0, nil, nil, 0x0047, nil, 0x0047],
-
0x0068 => [0, 0, nil, nil, 0x0048, nil, 0x0048],
-
0x0069 => [0, 0, nil, nil, 0x0049, nil, 0x0049],
-
0x006a => [0, 0, nil, nil, 0x004a, nil, 0x004a],
-
0x006b => [0, 0, nil, nil, 0x004b, nil, 0x004b],
-
0x006c => [0, 0, nil, nil, 0x004c, nil, 0x004c],
-
0x006d => [0, 0, nil, nil, 0x004d, nil, 0x004d],
-
0x006e => [0, 0, nil, nil, 0x004e, nil, 0x004e],
-
0x006f => [0, 0, nil, nil, 0x004f, nil, 0x004f],
-
0x0070 => [0, 0, nil, nil, 0x0050, nil, 0x0050],
-
0x0071 => [0, 0, nil, nil, 0x0051, nil, 0x0051],
-
0x0072 => [0, 0, nil, nil, 0x0052, nil, 0x0052],
-
0x0073 => [0, 0, nil, nil, 0x0053, nil, 0x0053],
-
0x0074 => [0, 0, nil, nil, 0x0054, nil, 0x0054],
-
0x0075 => [0, 0, nil, nil, 0x0055, nil, 0x0055],
-
0x0076 => [0, 0, nil, nil, 0x0056, nil, 0x0056],
-
0x0077 => [0, 0, nil, nil, 0x0057, nil, 0x0057],
-
0x0078 => [0, 0, nil, nil, 0x0058, nil, 0x0058],
-
0x0079 => [0, 0, nil, nil, 0x0059, nil, 0x0059],
-
0x007a => [0, 0, nil, nil, 0x005a, nil, 0x005a],
-
0x00a0 => [0, 0, nil, " ", nil, nil, nil],
-
0x00a8 => [0, 0, nil, " \314\210", nil, nil, nil],
-
0x00aa => [0, 0, nil, "a", nil, nil, nil],
-
0x00af => [0, 0, nil, " \314\204", nil, nil, nil],
-
0x00b2 => [0, 0, nil, "2", nil, nil, nil],
-
0x00b3 => [0, 0, nil, "3", nil, nil, nil],
-
0x00b4 => [0, 0, nil, " \314\201", nil, nil, nil],
-
0x00b5 => [0, 0, nil, "\316\274", 0x039c, nil, 0x039c],
-
0x00b8 => [0, 0, nil, " \314\247", nil, nil, nil],
-
0x00b9 => [0, 0, nil, "1", nil, nil, nil],
-
0x00ba => [0, 0, nil, "o", nil, nil, nil],
-
0x00bc => [0, 0, nil, "1\342\201\2044", nil, nil, nil],
-
0x00bd => [0, 0, nil, "1\342\201\2042", nil, nil, nil],
-
0x00be => [0, 0, nil, "3\342\201\2044", nil, nil, nil],
-
0x00c0 => [0, 0, "A\314\200", "A\314\200", nil, 0x00e0, nil],
-
0x00c1 => [0, 0, "A\314\201", "A\314\201", nil, 0x00e1, nil],
-
0x00c2 => [0, 0, "A\314\202", "A\314\202", nil, 0x00e2, nil],
-
0x00c3 => [0, 0, "A\314\203", "A\314\203", nil, 0x00e3, nil],
-
0x00c4 => [0, 0, "A\314\210", "A\314\210", nil, 0x00e4, nil],
-
0x00c5 => [0, 0, "A\314\212", "A\314\212", nil, 0x00e5, nil],
-
0x00c6 => [0, 0, nil, nil, nil, 0x00e6, nil],
-
0x00c7 => [0, 0, "C\314\247", "C\314\247", nil, 0x00e7, nil],
-
0x00c8 => [0, 0, "E\314\200", "E\314\200", nil, 0x00e8, nil],
-
0x00c9 => [0, 0, "E\314\201", "E\314\201", nil, 0x00e9, nil],
-
0x00ca => [0, 0, "E\314\202", "E\314\202", nil, 0x00ea, nil],
-
0x00cb => [0, 0, "E\314\210", "E\314\210", nil, 0x00eb, nil],
-
0x00cc => [0, 0, "I\314\200", "I\314\200", nil, 0x00ec, nil],
-
0x00cd => [0, 0, "I\314\201", "I\314\201", nil, 0x00ed, nil],
-
0x00ce => [0, 0, "I\314\202", "I\314\202", nil, 0x00ee, nil],
-
0x00cf => [0, 0, "I\314\210", "I\314\210", nil, 0x00ef, nil],
-
0x00d0 => [0, 0, nil, nil, nil, 0x00f0, nil],
-
0x00d1 => [0, 0, "N\314\203", "N\314\203", nil, 0x00f1, nil],
-
0x00d2 => [0, 0, "O\314\200", "O\314\200", nil, 0x00f2, nil],
-
0x00d3 => [0, 0, "O\314\201", "O\314\201", nil, 0x00f3, nil],
-
0x00d4 => [0, 0, "O\314\202", "O\314\202", nil, 0x00f4, nil],
-
0x00d5 => [0, 0, "O\314\203", "O\314\203", nil, 0x00f5, nil],
-
0x00d6 => [0, 0, "O\314\210", "O\314\210", nil, 0x00f6, nil],
-
0x00d8 => [0, 0, nil, nil, nil, 0x00f8, nil],
-
0x00d9 => [0, 0, "U\314\200", "U\314\200", nil, 0x00f9, nil],
-
0x00da => [0, 0, "U\314\201", "U\314\201", nil, 0x00fa, nil],
-
0x00db => [0, 0, "U\314\202", "U\314\202", nil, 0x00fb, nil],
-
0x00dc => [0, 0, "U\314\210", "U\314\210", nil, 0x00fc, nil],
-
0x00dd => [0, 0, "Y\314\201", "Y\314\201", nil, 0x00fd, nil],
-
0x00de => [0, 0, nil, nil, nil, 0x00fe, nil],
-
0x00e0 => [0, 0, "a\314\200", "a\314\200", 0x00c0, nil, 0x00c0],
-
0x00e1 => [0, 0, "a\314\201", "a\314\201", 0x00c1, nil, 0x00c1],
-
0x00e2 => [0, 0, "a\314\202", "a\314\202", 0x00c2, nil, 0x00c2],
-
0x00e3 => [0, 0, "a\314\203", "a\314\203", 0x00c3, nil, 0x00c3],
-
0x00e4 => [0, 0, "a\314\210", "a\314\210", 0x00c4, nil, 0x00c4],
-
0x00e5 => [0, 0, "a\314\212", "a\314\212", 0x00c5, nil, 0x00c5],
-
0x00e6 => [0, 0, nil, nil, 0x00c6, nil, 0x00c6],
-
0x00e7 => [0, 0, "c\314\247", "c\314\247", 0x00c7, nil, 0x00c7],
-
0x00e8 => [0, 0, "e\314\200", "e\314\200", 0x00c8, nil, 0x00c8],
-
0x00e9 => [0, 0, "e\314\201", "e\314\201", 0x00c9, nil, 0x00c9],
-
0x00ea => [0, 0, "e\314\202", "e\314\202", 0x00ca, nil, 0x00ca],
-
0x00eb => [0, 0, "e\314\210", "e\314\210", 0x00cb, nil, 0x00cb],
-
0x00ec => [0, 0, "i\314\200", "i\314\200", 0x00cc, nil, 0x00cc],
-
0x00ed => [0, 0, "i\314\201", "i\314\201", 0x00cd, nil, 0x00cd],
-
0x00ee => [0, 0, "i\314\202", "i\314\202", 0x00ce, nil, 0x00ce],
-
0x00ef => [0, 0, "i\314\210", "i\314\210", 0x00cf, nil, 0x00cf],
-
0x00f0 => [0, 0, nil, nil, 0x00d0, nil, 0x00d0],
-
0x00f1 => [0, 0, "n\314\203", "n\314\203", 0x00d1, nil, 0x00d1],
-
0x00f2 => [0, 0, "o\314\200", "o\314\200", 0x00d2, nil, 0x00d2],
-
0x00f3 => [0, 0, "o\314\201", "o\314\201", 0x00d3, nil, 0x00d3],
-
0x00f4 => [0, 0, "o\314\202", "o\314\202", 0x00d4, nil, 0x00d4],
-
0x00f5 => [0, 0, "o\314\203", "o\314\203", 0x00d5, nil, 0x00d5],
-
0x00f6 => [0, 0, "o\314\210", "o\314\210", 0x00d6, nil, 0x00d6],
-
0x00f8 => [0, 0, nil, nil, 0x00d8, nil, 0x00d8],
-
0x00f9 => [0, 0, "u\314\200", "u\314\200", 0x00d9, nil, 0x00d9],
-
0x00fa => [0, 0, "u\314\201", "u\314\201", 0x00da, nil, 0x00da],
-
0x00fb => [0, 0, "u\314\202", "u\314\202", 0x00db, nil, 0x00db],
-
0x00fc => [0, 0, "u\314\210", "u\314\210", 0x00dc, nil, 0x00dc],
-
0x00fd => [0, 0, "y\314\201", "y\314\201", 0x00dd, nil, 0x00dd],
-
0x00fe => [0, 0, nil, nil, 0x00de, nil, 0x00de],
-
0x00ff => [0, 0, "y\314\210", "y\314\210", 0x0178, nil, 0x0178],
-
0x0100 => [0, 0, "A\314\204", "A\314\204", nil, 0x0101, nil],
-
0x0101 => [0, 0, "a\314\204", "a\314\204", 0x0100, nil, 0x0100],
-
0x0102 => [0, 0, "A\314\206", "A\314\206", nil, 0x0103, nil],
-
0x0103 => [0, 0, "a\314\206", "a\314\206", 0x0102, nil, 0x0102],
-
0x0104 => [0, 0, "A\314\250", "A\314\250", nil, 0x0105, nil],
-
0x0105 => [0, 0, "a\314\250", "a\314\250", 0x0104, nil, 0x0104],
-
0x0106 => [0, 0, "C\314\201", "C\314\201", nil, 0x0107, nil],
-
0x0107 => [0, 0, "c\314\201", "c\314\201", 0x0106, nil, 0x0106],
-
0x0108 => [0, 0, "C\314\202", "C\314\202", nil, 0x0109, nil],
-
0x0109 => [0, 0, "c\314\202", "c\314\202", 0x0108, nil, 0x0108],
-
0x010a => [0, 0, "C\314\207", "C\314\207", nil, 0x010b, nil],
-
0x010b => [0, 0, "c\314\207", "c\314\207", 0x010a, nil, 0x010a],
-
0x010c => [0, 0, "C\314\214", "C\314\214", nil, 0x010d, nil],
-
0x010d => [0, 0, "c\314\214", "c\314\214", 0x010c, nil, 0x010c],
-
0x010e => [0, 0, "D\314\214", "D\314\214", nil, 0x010f, nil],
-
0x010f => [0, 0, "d\314\214", "d\314\214", 0x010e, nil, 0x010e],
-
0x0110 => [0, 0, nil, nil, nil, 0x0111, nil],
-
0x0111 => [0, 0, nil, nil, 0x0110, nil, 0x0110],
-
0x0112 => [0, 0, "E\314\204", "E\314\204", nil, 0x0113, nil],
-
0x0113 => [0, 0, "e\314\204", "e\314\204", 0x0112, nil, 0x0112],
-
0x0114 => [0, 0, "E\314\206", "E\314\206", nil, 0x0115, nil],
-
0x0115 => [0, 0, "e\314\206", "e\314\206", 0x0114, nil, 0x0114],
-
0x0116 => [0, 0, "E\314\207", "E\314\207", nil, 0x0117, nil],
-
0x0117 => [0, 0, "e\314\207", "e\314\207", 0x0116, nil, 0x0116],
-
0x0118 => [0, 0, "E\314\250", "E\314\250", nil, 0x0119, nil],
-
0x0119 => [0, 0, "e\314\250", "e\314\250", 0x0118, nil, 0x0118],
-
0x011a => [0, 0, "E\314\214", "E\314\214", nil, 0x011b, nil],
-
0x011b => [0, 0, "e\314\214", "e\314\214", 0x011a, nil, 0x011a],
-
0x011c => [0, 0, "G\314\202", "G\314\202", nil, 0x011d, nil],
-
0x011d => [0, 0, "g\314\202", "g\314\202", 0x011c, nil, 0x011c],
-
0x011e => [0, 0, "G\314\206", "G\314\206", nil, 0x011f, nil],
-
0x011f => [0, 0, "g\314\206", "g\314\206", 0x011e, nil, 0x011e],
-
0x0120 => [0, 0, "G\314\207", "G\314\207", nil, 0x0121, nil],
-
0x0121 => [0, 0, "g\314\207", "g\314\207", 0x0120, nil, 0x0120],
-
0x0122 => [0, 0, "G\314\247", "G\314\247", nil, 0x0123, nil],
-
0x0123 => [0, 0, "g\314\247", "g\314\247", 0x0122, nil, 0x0122],
-
0x0124 => [0, 0, "H\314\202", "H\314\202", nil, 0x0125, nil],
-
0x0125 => [0, 0, "h\314\202", "h\314\202", 0x0124, nil, 0x0124],
-
0x0126 => [0, 0, nil, nil, nil, 0x0127, nil],
-
0x0127 => [0, 0, nil, nil, 0x0126, nil, 0x0126],
-
0x0128 => [0, 0, "I\314\203", "I\314\203", nil, 0x0129, nil],
-
0x0129 => [0, 0, "i\314\203", "i\314\203", 0x0128, nil, 0x0128],
-
0x012a => [0, 0, "I\314\204", "I\314\204", nil, 0x012b, nil],
-
0x012b => [0, 0, "i\314\204", "i\314\204", 0x012a, nil, 0x012a],
-
0x012c => [0, 0, "I\314\206", "I\314\206", nil, 0x012d, nil],
-
0x012d => [0, 0, "i\314\206", "i\314\206", 0x012c, nil, 0x012c],
-
0x012e => [0, 0, "I\314\250", "I\314\250", nil, 0x012f, nil],
-
0x012f => [0, 0, "i\314\250", "i\314\250", 0x012e, nil, 0x012e],
-
0x0130 => [0, 0, "I\314\207", "I\314\207", nil, 0x0069, nil],
-
0x0131 => [0, 0, nil, nil, 0x0049, nil, 0x0049],
-
0x0132 => [0, 0, nil, "IJ", nil, 0x0133, nil],
-
0x0133 => [0, 0, nil, "ij", 0x0132, nil, 0x0132],
-
0x0134 => [0, 0, "J\314\202", "J\314\202", nil, 0x0135, nil],
-
0x0135 => [0, 0, "j\314\202", "j\314\202", 0x0134, nil, 0x0134],
-
0x0136 => [0, 0, "K\314\247", "K\314\247", nil, 0x0137, nil],
-
0x0137 => [0, 0, "k\314\247", "k\314\247", 0x0136, nil, 0x0136],
-
0x0139 => [0, 0, "L\314\201", "L\314\201", nil, 0x013a, nil],
-
0x013a => [0, 0, "l\314\201", "l\314\201", 0x0139, nil, 0x0139],
-
0x013b => [0, 0, "L\314\247", "L\314\247", nil, 0x013c, nil],
-
0x013c => [0, 0, "l\314\247", "l\314\247", 0x013b, nil, 0x013b],
-
0x013d => [0, 0, "L\314\214", "L\314\214", nil, 0x013e, nil],
-
0x013e => [0, 0, "l\314\214", "l\314\214", 0x013d, nil, 0x013d],
-
0x013f => [0, 0, nil, "L\302\267", nil, 0x0140, nil],
-
0x0140 => [0, 0, nil, "l\302\267", 0x013f, nil, 0x013f],
-
0x0141 => [0, 0, nil, nil, nil, 0x0142, nil],
-
0x0142 => [0, 0, nil, nil, 0x0141, nil, 0x0141],
-
0x0143 => [0, 0, "N\314\201", "N\314\201", nil, 0x0144, nil],
-
0x0144 => [0, 0, "n\314\201", "n\314\201", 0x0143, nil, 0x0143],
-
0x0145 => [0, 0, "N\314\247", "N\314\247", nil, 0x0146, nil],
-
0x0146 => [0, 0, "n\314\247", "n\314\247", 0x0145, nil, 0x0145],
-
0x0147 => [0, 0, "N\314\214", "N\314\214", nil, 0x0148, nil],
-
0x0148 => [0, 0, "n\314\214", "n\314\214", 0x0147, nil, 0x0147],
-
0x0149 => [0, 0, nil, "\312\274n", nil, nil, nil],
-
0x014a => [0, 0, nil, nil, nil, 0x014b, nil],
-
0x014b => [0, 0, nil, nil, 0x014a, nil, 0x014a],
-
0x014c => [0, 0, "O\314\204", "O\314\204", nil, 0x014d, nil],
-
0x014d => [0, 0, "o\314\204", "o\314\204", 0x014c, nil, 0x014c],
-
0x014e => [0, 0, "O\314\206", "O\314\206", nil, 0x014f, nil],
-
0x014f => [0, 0, "o\314\206", "o\314\206", 0x014e, nil, 0x014e],
-
0x0150 => [0, 0, "O\314\213", "O\314\213", nil, 0x0151, nil],
-
0x0151 => [0, 0, "o\314\213", "o\314\213", 0x0150, nil, 0x0150],
-
0x0152 => [0, 0, nil, nil, nil, 0x0153, nil],
-
0x0153 => [0, 0, nil, nil, 0x0152, nil, 0x0152],
-
0x0154 => [0, 0, "R\314\201", "R\314\201", nil, 0x0155, nil],
-
0x0155 => [0, 0, "r\314\201", "r\314\201", 0x0154, nil, 0x0154],
-
0x0156 => [0, 0, "R\314\247", "R\314\247", nil, 0x0157, nil],
-
0x0157 => [0, 0, "r\314\247", "r\314\247", 0x0156, nil, 0x0156],
-
0x0158 => [0, 0, "R\314\214", "R\314\214", nil, 0x0159, nil],
-
0x0159 => [0, 0, "r\314\214", "r\314\214", 0x0158, nil, 0x0158],
-
0x015a => [0, 0, "S\314\201", "S\314\201", nil, 0x015b, nil],
-
0x015b => [0, 0, "s\314\201", "s\314\201", 0x015a, nil, 0x015a],
-
0x015c => [0, 0, "S\314\202", "S\314\202", nil, 0x015d, nil],
-
0x015d => [0, 0, "s\314\202", "s\314\202", 0x015c, nil, 0x015c],
-
0x015e => [0, 0, "S\314\247", "S\314\247", nil, 0x015f, nil],
-
0x015f => [0, 0, "s\314\247", "s\314\247", 0x015e, nil, 0x015e],
-
0x0160 => [0, 0, "S\314\214", "S\314\214", nil, 0x0161, nil],
-
0x0161 => [0, 0, "s\314\214", "s\314\214", 0x0160, nil, 0x0160],
-
0x0162 => [0, 0, "T\314\247", "T\314\247", nil, 0x0163, nil],
-
0x0163 => [0, 0, "t\314\247", "t\314\247", 0x0162, nil, 0x0162],
-
0x0164 => [0, 0, "T\314\214", "T\314\214", nil, 0x0165, nil],
-
0x0165 => [0, 0, "t\314\214", "t\314\214", 0x0164, nil, 0x0164],
-
0x0166 => [0, 0, nil, nil, nil, 0x0167, nil],
-
0x0167 => [0, 0, nil, nil, 0x0166, nil, 0x0166],
-
0x0168 => [0, 0, "U\314\203", "U\314\203", nil, 0x0169, nil],
-
0x0169 => [0, 0, "u\314\203", "u\314\203", 0x0168, nil, 0x0168],
-
0x016a => [0, 0, "U\314\204", "U\314\204", nil, 0x016b, nil],
-
0x016b => [0, 0, "u\314\204", "u\314\204", 0x016a, nil, 0x016a],
-
0x016c => [0, 0, "U\314\206", "U\314\206", nil, 0x016d, nil],
-
0x016d => [0, 0, "u\314\206", "u\314\206", 0x016c, nil, 0x016c],
-
0x016e => [0, 0, "U\314\212", "U\314\212", nil, 0x016f, nil],
-
0x016f => [0, 0, "u\314\212", "u\314\212", 0x016e, nil, 0x016e],
-
0x0170 => [0, 0, "U\314\213", "U\314\213", nil, 0x0171, nil],
-
0x0171 => [0, 0, "u\314\213", "u\314\213", 0x0170, nil, 0x0170],
-
0x0172 => [0, 0, "U\314\250", "U\314\250", nil, 0x0173, nil],
-
0x0173 => [0, 0, "u\314\250", "u\314\250", 0x0172, nil, 0x0172],
-
0x0174 => [0, 0, "W\314\202", "W\314\202", nil, 0x0175, nil],
-
0x0175 => [0, 0, "w\314\202", "w\314\202", 0x0174, nil, 0x0174],
-
0x0176 => [0, 0, "Y\314\202", "Y\314\202", nil, 0x0177, nil],
-
0x0177 => [0, 0, "y\314\202", "y\314\202", 0x0176, nil, 0x0176],
-
0x0178 => [0, 0, "Y\314\210", "Y\314\210", nil, 0x00ff, nil],
-
0x0179 => [0, 0, "Z\314\201", "Z\314\201", nil, 0x017a, nil],
-
0x017a => [0, 0, "z\314\201", "z\314\201", 0x0179, nil, 0x0179],
-
0x017b => [0, 0, "Z\314\207", "Z\314\207", nil, 0x017c, nil],
-
0x017c => [0, 0, "z\314\207", "z\314\207", 0x017b, nil, 0x017b],
-
0x017d => [0, 0, "Z\314\214", "Z\314\214", nil, 0x017e, nil],
-
0x017e => [0, 0, "z\314\214", "z\314\214", 0x017d, nil, 0x017d],
-
0x017f => [0, 0, nil, "s", 0x0053, nil, 0x0053],
-
0x0181 => [0, 0, nil, nil, nil, 0x0253, nil],
-
0x0182 => [0, 0, nil, nil, nil, 0x0183, nil],
-
0x0183 => [0, 0, nil, nil, 0x0182, nil, 0x0182],
-
0x0184 => [0, 0, nil, nil, nil, 0x0185, nil],
-
0x0185 => [0, 0, nil, nil, 0x0184, nil, 0x0184],
-
0x0186 => [0, 0, nil, nil, nil, 0x0254, nil],
-
0x0187 => [0, 0, nil, nil, nil, 0x0188, nil],
-
0x0188 => [0, 0, nil, nil, 0x0187, nil, 0x0187],
-
0x0189 => [0, 0, nil, nil, nil, 0x0256, nil],
-
0x018a => [0, 0, nil, nil, nil, 0x0257, nil],
-
0x018b => [0, 0, nil, nil, nil, 0x018c, nil],
-
0x018c => [0, 0, nil, nil, 0x018b, nil, 0x018b],
-
0x018e => [0, 0, nil, nil, nil, 0x01dd, nil],
-
0x018f => [0, 0, nil, nil, nil, 0x0259, nil],
-
0x0190 => [0, 0, nil, nil, nil, 0x025b, nil],
-
0x0191 => [0, 0, nil, nil, nil, 0x0192, nil],
-
0x0192 => [0, 0, nil, nil, 0x0191, nil, 0x0191],
-
0x0193 => [0, 0, nil, nil, nil, 0x0260, nil],
-
0x0194 => [0, 0, nil, nil, nil, 0x0263, nil],
-
0x0195 => [0, 0, nil, nil, 0x01f6, nil, 0x01f6],
-
0x0196 => [0, 0, nil, nil, nil, 0x0269, nil],
-
0x0197 => [0, 0, nil, nil, nil, 0x0268, nil],
-
0x0198 => [0, 0, nil, nil, nil, 0x0199, nil],
-
0x0199 => [0, 0, nil, nil, 0x0198, nil, 0x0198],
-
0x019c => [0, 0, nil, nil, nil, 0x026f, nil],
-
0x019d => [0, 0, nil, nil, nil, 0x0272, nil],
-
0x019f => [0, 0, nil, nil, nil, 0x0275, nil],
-
0x01a0 => [0, 0, "O\314\233", "O\314\233", nil, 0x01a1, nil],
-
0x01a1 => [0, 0, "o\314\233", "o\314\233", 0x01a0, nil, 0x01a0],
-
0x01a2 => [0, 0, nil, nil, nil, 0x01a3, nil],
-
0x01a3 => [0, 0, nil, nil, 0x01a2, nil, 0x01a2],
-
0x01a4 => [0, 0, nil, nil, nil, 0x01a5, nil],
-
0x01a5 => [0, 0, nil, nil, 0x01a4, nil, 0x01a4],
-
0x01a6 => [0, 0, nil, nil, nil, 0x0280, nil],
-
0x01a7 => [0, 0, nil, nil, nil, 0x01a8, nil],
-
0x01a8 => [0, 0, nil, nil, 0x01a7, nil, 0x01a7],
-
0x01a9 => [0, 0, nil, nil, nil, 0x0283, nil],
-
0x01ac => [0, 0, nil, nil, nil, 0x01ad, nil],
-
0x01ad => [0, 0, nil, nil, 0x01ac, nil, 0x01ac],
-
0x01ae => [0, 0, nil, nil, nil, 0x0288, nil],
-
0x01af => [0, 0, "U\314\233", "U\314\233", nil, 0x01b0, nil],
-
0x01b0 => [0, 0, "u\314\233", "u\314\233", 0x01af, nil, 0x01af],
-
0x01b1 => [0, 0, nil, nil, nil, 0x028a, nil],
-
0x01b2 => [0, 0, nil, nil, nil, 0x028b, nil],
-
0x01b3 => [0, 0, nil, nil, nil, 0x01b4, nil],
-
0x01b4 => [0, 0, nil, nil, 0x01b3, nil, 0x01b3],
-
0x01b5 => [0, 0, nil, nil, nil, 0x01b6, nil],
-
0x01b6 => [0, 0, nil, nil, 0x01b5, nil, 0x01b5],
-
0x01b7 => [0, 0, nil, nil, nil, 0x0292, nil],
-
0x01b8 => [0, 0, nil, nil, nil, 0x01b9, nil],
-
0x01b9 => [0, 0, nil, nil, 0x01b8, nil, 0x01b8],
-
0x01bc => [0, 0, nil, nil, nil, 0x01bd, nil],
-
0x01bd => [0, 0, nil, nil, 0x01bc, nil, 0x01bc],
-
0x01bf => [0, 0, nil, nil, 0x01f7, nil, 0x01f7],
-
0x01c4 => [0, 0, nil, "D\305\275", nil, 0x01c6, 0x01c5],
-
0x01c5 => [0, 0, nil, "D\305\276", 0x01c4, 0x01c6, nil],
-
0x01c6 => [0, 0, nil, "d\305\276", 0x01c4, nil, 0x01c5],
-
0x01c7 => [0, 0, nil, "LJ", nil, 0x01c9, 0x01c8],
-
0x01c8 => [0, 0, nil, "Lj", 0x01c7, 0x01c9, nil],
-
0x01c9 => [0, 0, nil, "lj", 0x01c7, nil, 0x01c8],
-
0x01ca => [0, 0, nil, "NJ", nil, 0x01cc, 0x01cb],
-
0x01cb => [0, 0, nil, "Nj", 0x01ca, 0x01cc, nil],
-
0x01cc => [0, 0, nil, "nj", 0x01ca, nil, 0x01cb],
-
0x01cd => [0, 0, "A\314\214", "A\314\214", nil, 0x01ce, nil],
-
0x01ce => [0, 0, "a\314\214", "a\314\214", 0x01cd, nil, 0x01cd],
-
0x01cf => [0, 0, "I\314\214", "I\314\214", nil, 0x01d0, nil],
-
0x01d0 => [0, 0, "i\314\214", "i\314\214", 0x01cf, nil, 0x01cf],
-
0x01d1 => [0, 0, "O\314\214", "O\314\214", nil, 0x01d2, nil],
-
0x01d2 => [0, 0, "o\314\214", "o\314\214", 0x01d1, nil, 0x01d1],
-
0x01d3 => [0, 0, "U\314\214", "U\314\214", nil, 0x01d4, nil],
-
0x01d4 => [0, 0, "u\314\214", "u\314\214", 0x01d3, nil, 0x01d3],
-
0x01d5 => [0, 0, "\303\234\314\204", "\303\234\314\204", nil, 0x01d6, nil],
-
0x01d6 => [0, 0, "\303\274\314\204", "\303\274\314\204", 0x01d5, nil, 0x01d5],
-
0x01d7 => [0, 0, "\303\234\314\201", "\303\234\314\201", nil, 0x01d8, nil],
-
0x01d8 => [0, 0, "\303\274\314\201", "\303\274\314\201", 0x01d7, nil, 0x01d7],
-
0x01d9 => [0, 0, "\303\234\314\214", "\303\234\314\214", nil, 0x01da, nil],
-
0x01da => [0, 0, "\303\274\314\214", "\303\274\314\214", 0x01d9, nil, 0x01d9],
-
0x01db => [0, 0, "\303\234\314\200", "\303\234\314\200", nil, 0x01dc, nil],
-
0x01dc => [0, 0, "\303\274\314\200", "\303\274\314\200", 0x01db, nil, 0x01db],
-
0x01dd => [0, 0, nil, nil, 0x018e, nil, 0x018e],
-
0x01de => [0, 0, "\303\204\314\204", "\303\204\314\204", nil, 0x01df, nil],
-
0x01df => [0, 0, "\303\244\314\204", "\303\244\314\204", 0x01de, nil, 0x01de],
-
0x01e0 => [0, 0, "\310\246\314\204", "\310\246\314\204", nil, 0x01e1, nil],
-
0x01e1 => [0, 0, "\310\247\314\204", "\310\247\314\204", 0x01e0, nil, 0x01e0],
-
0x01e2 => [0, 0, "\303\206\314\204", "\303\206\314\204", nil, 0x01e3, nil],
-
0x01e3 => [0, 0, "\303\246\314\204", "\303\246\314\204", 0x01e2, nil, 0x01e2],
-
0x01e4 => [0, 0, nil, nil, nil, 0x01e5, nil],
-
0x01e5 => [0, 0, nil, nil, 0x01e4, nil, 0x01e4],
-
0x01e6 => [0, 0, "G\314\214", "G\314\214", nil, 0x01e7, nil],
-
0x01e7 => [0, 0, "g\314\214", "g\314\214", 0x01e6, nil, 0x01e6],
-
0x01e8 => [0, 0, "K\314\214", "K\314\214", nil, 0x01e9, nil],
-
0x01e9 => [0, 0, "k\314\214", "k\314\214", 0x01e8, nil, 0x01e8],
-
0x01ea => [0, 0, "O\314\250", "O\314\250", nil, 0x01eb, nil],
-
0x01eb => [0, 0, "o\314\250", "o\314\250", 0x01ea, nil, 0x01ea],
-
0x01ec => [0, 0, "\307\252\314\204", "\307\252\314\204", nil, 0x01ed, nil],
-
0x01ed => [0, 0, "\307\253\314\204", "\307\253\314\204", 0x01ec, nil, 0x01ec],
-
0x01ee => [0, 0, "\306\267\314\214", "\306\267\314\214", nil, 0x01ef, nil],
-
0x01ef => [0, 0, "\312\222\314\214", "\312\222\314\214", 0x01ee, nil, 0x01ee],
-
0x01f0 => [0, 0, "j\314\214", "j\314\214", nil, nil, nil],
-
0x01f1 => [0, 0, nil, "DZ", nil, 0x01f3, 0x01f2],
-
0x01f2 => [0, 0, nil, "Dz", 0x01f1, 0x01f3, nil],
-
0x01f3 => [0, 0, nil, "dz", 0x01f1, nil, 0x01f2],
-
0x01f4 => [0, 0, "G\314\201", "G\314\201", nil, 0x01f5, nil],
-
0x01f5 => [0, 0, "g\314\201", "g\314\201", 0x01f4, nil, 0x01f4],
-
0x01f6 => [0, 0, nil, nil, nil, 0x0195, nil],
-
0x01f7 => [0, 0, nil, nil, nil, 0x01bf, nil],
-
0x01f8 => [0, 0, "N\314\200", "N\314\200", nil, 0x01f9, nil],
-
0x01f9 => [0, 0, "n\314\200", "n\314\200", 0x01f8, nil, 0x01f8],
-
0x01fa => [0, 0, "\303\205\314\201", "\303\205\314\201", nil, 0x01fb, nil],
-
0x01fb => [0, 0, "\303\245\314\201", "\303\245\314\201", 0x01fa, nil, 0x01fa],
-
0x01fc => [0, 0, "\303\206\314\201", "\303\206\314\201", nil, 0x01fd, nil],
-
0x01fd => [0, 0, "\303\246\314\201", "\303\246\314\201", 0x01fc, nil, 0x01fc],
-
0x01fe => [0, 0, "\303\230\314\201", "\303\230\314\201", nil, 0x01ff, nil],
-
0x01ff => [0, 0, "\303\270\314\201", "\303\270\314\201", 0x01fe, nil, 0x01fe],
-
0x0200 => [0, 0, "A\314\217", "A\314\217", nil, 0x0201, nil],
-
0x0201 => [0, 0, "a\314\217", "a\314\217", 0x0200, nil, 0x0200],
-
0x0202 => [0, 0, "A\314\221", "A\314\221", nil, 0x0203, nil],
-
0x0203 => [0, 0, "a\314\221", "a\314\221", 0x0202, nil, 0x0202],
-
0x0204 => [0, 0, "E\314\217", "E\314\217", nil, 0x0205, nil],
-
0x0205 => [0, 0, "e\314\217", "e\314\217", 0x0204, nil, 0x0204],
-
0x0206 => [0, 0, "E\314\221", "E\314\221", nil, 0x0207, nil],
-
0x0207 => [0, 0, "e\314\221", "e\314\221", 0x0206, nil, 0x0206],
-
0x0208 => [0, 0, "I\314\217", "I\314\217", nil, 0x0209, nil],
-
0x0209 => [0, 0, "i\314\217", "i\314\217", 0x0208, nil, 0x0208],
-
0x020a => [0, 0, "I\314\221", "I\314\221", nil, 0x020b, nil],
-
0x020b => [0, 0, "i\314\221", "i\314\221", 0x020a, nil, 0x020a],
-
0x020c => [0, 0, "O\314\217", "O\314\217", nil, 0x020d, nil],
-
0x020d => [0, 0, "o\314\217", "o\314\217", 0x020c, nil, 0x020c],
-
0x020e => [0, 0, "O\314\221", "O\314\221", nil, 0x020f, nil],
-
0x020f => [0, 0, "o\314\221", "o\314\221", 0x020e, nil, 0x020e],
-
0x0210 => [0, 0, "R\314\217", "R\314\217", nil, 0x0211, nil],
-
0x0211 => [0, 0, "r\314\217", "r\314\217", 0x0210, nil, 0x0210],
-
0x0212 => [0, 0, "R\314\221", "R\314\221", nil, 0x0213, nil],
-
0x0213 => [0, 0, "r\314\221", "r\314\221", 0x0212, nil, 0x0212],
-
0x0214 => [0, 0, "U\314\217", "U\314\217", nil, 0x0215, nil],
-
0x0215 => [0, 0, "u\314\217", "u\314\217", 0x0214, nil, 0x0214],
-
0x0216 => [0, 0, "U\314\221", "U\314\221", nil, 0x0217, nil],
-
0x0217 => [0, 0, "u\314\221", "u\314\221", 0x0216, nil, 0x0216],
-
0x0218 => [0, 0, "S\314\246", "S\314\246", nil, 0x0219, nil],
-
0x0219 => [0, 0, "s\314\246", "s\314\246", 0x0218, nil, 0x0218],
-
0x021a => [0, 0, "T\314\246", "T\314\246", nil, 0x021b, nil],
-
0x021b => [0, 0, "t\314\246", "t\314\246", 0x021a, nil, 0x021a],
-
0x021c => [0, 0, nil, nil, nil, 0x021d, nil],
-
0x021d => [0, 0, nil, nil, 0x021c, nil, 0x021c],
-
0x021e => [0, 0, "H\314\214", "H\314\214", nil, 0x021f, nil],
-
0x021f => [0, 0, "h\314\214", "h\314\214", 0x021e, nil, 0x021e],
-
0x0222 => [0, 0, nil, nil, nil, 0x0223, nil],
-
0x0223 => [0, 0, nil, nil, 0x0222, nil, 0x0222],
-
0x0224 => [0, 0, nil, nil, nil, 0x0225, nil],
-
0x0225 => [0, 0, nil, nil, 0x0224, nil, 0x0224],
-
0x0226 => [0, 0, "A\314\207", "A\314\207", nil, 0x0227, nil],
-
0x0227 => [0, 0, "a\314\207", "a\314\207", 0x0226, nil, 0x0226],
-
0x0228 => [0, 0, "E\314\247", "E\314\247", nil, 0x0229, nil],
-
0x0229 => [0, 0, "e\314\247", "e\314\247", 0x0228, nil, 0x0228],
-
0x022a => [0, 0, "\303\226\314\204", "\303\226\314\204", nil, 0x022b, nil],
-
0x022b => [0, 0, "\303\266\314\204", "\303\266\314\204", 0x022a, nil, 0x022a],
-
0x022c => [0, 0, "\303\225\314\204", "\303\225\314\204", nil, 0x022d, nil],
-
0x022d => [0, 0, "\303\265\314\204", "\303\265\314\204", 0x022c, nil, 0x022c],
-
0x022e => [0, 0, "O\314\207", "O\314\207", nil, 0x022f, nil],
-
0x022f => [0, 0, "o\314\207", "o\314\207", 0x022e, nil, 0x022e],
-
0x0230 => [0, 0, "\310\256\314\204", "\310\256\314\204", nil, 0x0231, nil],
-
0x0231 => [0, 0, "\310\257\314\204", "\310\257\314\204", 0x0230, nil, 0x0230],
-
0x0232 => [0, 0, "Y\314\204", "Y\314\204", nil, 0x0233, nil],
-
0x0233 => [0, 0, "y\314\204", "y\314\204", 0x0232, nil, 0x0232],
-
0x0253 => [0, 0, nil, nil, 0x0181, nil, 0x0181],
-
0x0254 => [0, 0, nil, nil, 0x0186, nil, 0x0186],
-
0x0256 => [0, 0, nil, nil, 0x0189, nil, 0x0189],
-
0x0257 => [0, 0, nil, nil, 0x018a, nil, 0x018a],
-
0x0259 => [0, 0, nil, nil, 0x018f, nil, 0x018f],
-
0x025b => [0, 0, nil, nil, 0x0190, nil, 0x0190],
-
0x0260 => [0, 0, nil, nil, 0x0193, nil, 0x0193],
-
0x0263 => [0, 0, nil, nil, 0x0194, nil, 0x0194],
-
0x0268 => [0, 0, nil, nil, 0x0197, nil, 0x0197],
-
0x0269 => [0, 0, nil, nil, 0x0196, nil, 0x0196],
-
0x026f => [0, 0, nil, nil, 0x019c, nil, 0x019c],
-
0x0272 => [0, 0, nil, nil, 0x019d, nil, 0x019d],
-
0x0275 => [0, 0, nil, nil, 0x019f, nil, 0x019f],
-
0x0280 => [0, 0, nil, nil, 0x01a6, nil, 0x01a6],
-
0x0283 => [0, 0, nil, nil, 0x01a9, nil, 0x01a9],
-
0x0288 => [0, 0, nil, nil, 0x01ae, nil, 0x01ae],
-
0x028a => [0, 0, nil, nil, 0x01b1, nil, 0x01b1],
-
0x028b => [0, 0, nil, nil, 0x01b2, nil, 0x01b2],
-
0x0292 => [0, 0, nil, nil, 0x01b7, nil, 0x01b7],
-
0x02b0 => [0, 0, nil, "h", nil, nil, nil],
-
0x02b1 => [0, 0, nil, "\311\246", nil, nil, nil],
-
0x02b2 => [0, 0, nil, "j", nil, nil, nil],
-
0x02b3 => [0, 0, nil, "r", nil, nil, nil],
-
0x02b4 => [0, 0, nil, "\311\271", nil, nil, nil],
-
0x02b5 => [0, 0, nil, "\311\273", nil, nil, nil],
-
0x02b6 => [0, 0, nil, "\312\201", nil, nil, nil],
-
0x02b7 => [0, 0, nil, "w", nil, nil, nil],
-
0x02b8 => [0, 0, nil, "y", nil, nil, nil],
-
0x02d8 => [0, 0, nil, " \314\206", nil, nil, nil],
-
0x02d9 => [0, 0, nil, " \314\207", nil, nil, nil],
-
0x02da => [0, 0, nil, " \314\212", nil, nil, nil],
-
0x02db => [0, 0, nil, " \314\250", nil, nil, nil],
-
0x02dc => [0, 0, nil, " \314\203", nil, nil, nil],
-
0x02dd => [0, 0, nil, " \314\213", nil, nil, nil],
-
0x02e0 => [0, 0, nil, "\311\243", nil, nil, nil],
-
0x02e1 => [0, 0, nil, "l", nil, nil, nil],
-
0x02e2 => [0, 0, nil, "s", nil, nil, nil],
-
0x02e3 => [0, 0, nil, "x", nil, nil, nil],
-
0x02e4 => [0, 0, nil, "\312\225", nil, nil, nil],
-
0x0300 => [230, 0, nil, nil, nil, nil, nil],
-
0x0301 => [230, 0, nil, nil, nil, nil, nil],
-
0x0302 => [230, 0, nil, nil, nil, nil, nil],
-
0x0303 => [230, 0, nil, nil, nil, nil, nil],
-
0x0304 => [230, 0, nil, nil, nil, nil, nil],
-
0x0305 => [230, 0, nil, nil, nil, nil, nil],
-
0x0306 => [230, 0, nil, nil, nil, nil, nil],
-
0x0307 => [230, 0, nil, nil, nil, nil, nil],
-
0x0308 => [230, 0, nil, nil, nil, nil, nil],
-
0x0309 => [230, 0, nil, nil, nil, nil, nil],
-
0x030a => [230, 0, nil, nil, nil, nil, nil],
-
0x030b => [230, 0, nil, nil, nil, nil, nil],
-
0x030c => [230, 0, nil, nil, nil, nil, nil],
-
0x030d => [230, 0, nil, nil, nil, nil, nil],
-
0x030e => [230, 0, nil, nil, nil, nil, nil],
-
0x030f => [230, 0, nil, nil, nil, nil, nil],
-
0x0310 => [230, 0, nil, nil, nil, nil, nil],
-
0x0311 => [230, 0, nil, nil, nil, nil, nil],
-
0x0312 => [230, 0, nil, nil, nil, nil, nil],
-
0x0313 => [230, 0, nil, nil, nil, nil, nil],
-
0x0314 => [230, 0, nil, nil, nil, nil, nil],
-
0x0315 => [232, 0, nil, nil, nil, nil, nil],
-
0x0316 => [220, 0, nil, nil, nil, nil, nil],
-
0x0317 => [220, 0, nil, nil, nil, nil, nil],
-
0x0318 => [220, 0, nil, nil, nil, nil, nil],
-
0x0319 => [220, 0, nil, nil, nil, nil, nil],
-
0x031a => [232, 0, nil, nil, nil, nil, nil],
-
0x031b => [216, 0, nil, nil, nil, nil, nil],
-
0x031c => [220, 0, nil, nil, nil, nil, nil],
-
0x031d => [220, 0, nil, nil, nil, nil, nil],
-
0x031e => [220, 0, nil, nil, nil, nil, nil],
-
0x031f => [220, 0, nil, nil, nil, nil, nil],
-
0x0320 => [220, 0, nil, nil, nil, nil, nil],
-
0x0321 => [202, 0, nil, nil, nil, nil, nil],
-
0x0322 => [202, 0, nil, nil, nil, nil, nil],
-
0x0323 => [220, 0, nil, nil, nil, nil, nil],
-
0x0324 => [220, 0, nil, nil, nil, nil, nil],
-
0x0325 => [220, 0, nil, nil, nil, nil, nil],
-
0x0326 => [220, 0, nil, nil, nil, nil, nil],
-
0x0327 => [202, 0, nil, nil, nil, nil, nil],
-
0x0328 => [202, 0, nil, nil, nil, nil, nil],
-
0x0329 => [220, 0, nil, nil, nil, nil, nil],
-
0x032a => [220, 0, nil, nil, nil, nil, nil],
-
0x032b => [220, 0, nil, nil, nil, nil, nil],
-
0x032c => [220, 0, nil, nil, nil, nil, nil],
-
0x032d => [220, 0, nil, nil, nil, nil, nil],
-
0x032e => [220, 0, nil, nil, nil, nil, nil],
-
0x032f => [220, 0, nil, nil, nil, nil, nil],
-
0x0330 => [220, 0, nil, nil, nil, nil, nil],
-
0x0331 => [220, 0, nil, nil, nil, nil, nil],
-
0x0332 => [220, 0, nil, nil, nil, nil, nil],
-
0x0333 => [220, 0, nil, nil, nil, nil, nil],
-
0x0334 => [1, 0, nil, nil, nil, nil, nil],
-
0x0335 => [1, 0, nil, nil, nil, nil, nil],
-
0x0336 => [1, 0, nil, nil, nil, nil, nil],
-
0x0337 => [1, 0, nil, nil, nil, nil, nil],
-
0x0338 => [1, 0, nil, nil, nil, nil, nil],
-
0x0339 => [220, 0, nil, nil, nil, nil, nil],
-
0x033a => [220, 0, nil, nil, nil, nil, nil],
-
0x033b => [220, 0, nil, nil, nil, nil, nil],
-
0x033c => [220, 0, nil, nil, nil, nil, nil],
-
0x033d => [230, 0, nil, nil, nil, nil, nil],
-
0x033e => [230, 0, nil, nil, nil, nil, nil],
-
0x033f => [230, 0, nil, nil, nil, nil, nil],
-
0x0340 => [230, 2, "\314\200", "\314\200", nil, nil, nil],
-
0x0341 => [230, 2, "\314\201", "\314\201", nil, nil, nil],
-
0x0342 => [230, 0, nil, nil, nil, nil, nil],
-
0x0343 => [230, 2, "\314\223", "\314\223", nil, nil, nil],
-
0x0344 => [230, 3, "\314\210\314\201", "\314\210\314\201", nil, nil, nil],
-
0x0345 => [240, 0, nil, nil, 0x0399, nil, 0x0399],
-
0x0346 => [230, 0, nil, nil, nil, nil, nil],
-
0x0347 => [220, 0, nil, nil, nil, nil, nil],
-
0x0348 => [220, 0, nil, nil, nil, nil, nil],
-
0x0349 => [220, 0, nil, nil, nil, nil, nil],
-
0x034a => [230, 0, nil, nil, nil, nil, nil],
-
0x034b => [230, 0, nil, nil, nil, nil, nil],
-
0x034c => [230, 0, nil, nil, nil, nil, nil],
-
0x034d => [220, 0, nil, nil, nil, nil, nil],
-
0x034e => [220, 0, nil, nil, nil, nil, nil],
-
0x0360 => [234, 0, nil, nil, nil, nil, nil],
-
0x0361 => [234, 0, nil, nil, nil, nil, nil],
-
0x0362 => [233, 0, nil, nil, nil, nil, nil],
-
0x0374 => [0, 2, "\312\271", "\312\271", nil, nil, nil],
-
0x037a => [0, 0, nil, " \315\205", nil, nil, nil],
-
0x037e => [0, 2, ";", ";", nil, nil, nil],
-
0x0384 => [0, 0, nil, " \314\201", nil, nil, nil],
-
0x0385 => [0, 0, "\302\250\314\201", "\302\250\314\201", nil, nil, nil],
-
0x0386 => [0, 0, "\316\221\314\201", "\316\221\314\201", nil, 0x03ac, nil],
-
0x0387 => [0, 2, "\302\267", "\302\267", nil, nil, nil],
-
0x0388 => [0, 0, "\316\225\314\201", "\316\225\314\201", nil, 0x03ad, nil],
-
0x0389 => [0, 0, "\316\227\314\201", "\316\227\314\201", nil, 0x03ae, nil],
-
0x038a => [0, 0, "\316\231\314\201", "\316\231\314\201", nil, 0x03af, nil],
-
0x038c => [0, 0, "\316\237\314\201", "\316\237\314\201", nil, 0x03cc, nil],
-
0x038e => [0, 0, "\316\245\314\201", "\316\245\314\201", nil, 0x03cd, nil],
-
0x038f => [0, 0, "\316\251\314\201", "\316\251\314\201", nil, 0x03ce, nil],
-
0x0390 => [0, 0, "\317\212\314\201", "\317\212\314\201", nil, nil, nil],
-
0x0391 => [0, 0, nil, nil, nil, 0x03b1, nil],
-
0x0392 => [0, 0, nil, nil, nil, 0x03b2, nil],
-
0x0393 => [0, 0, nil, nil, nil, 0x03b3, nil],
-
0x0394 => [0, 0, nil, nil, nil, 0x03b4, nil],
-
0x0395 => [0, 0, nil, nil, nil, 0x03b5, nil],
-
0x0396 => [0, 0, nil, nil, nil, 0x03b6, nil],
-
0x0397 => [0, 0, nil, nil, nil, 0x03b7, nil],
-
0x0398 => [0, 0, nil, nil, nil, 0x03b8, nil],
-
0x0399 => [0, 0, nil, nil, nil, 0x03b9, nil],
-
0x039a => [0, 0, nil, nil, nil, 0x03ba, nil],
-
0x039b => [0, 0, nil, nil, nil, 0x03bb, nil],
-
0x039c => [0, 0, nil, nil, nil, 0x03bc, nil],
-
0x039d => [0, 0, nil, nil, nil, 0x03bd, nil],
-
0x039e => [0, 0, nil, nil, nil, 0x03be, nil],
-
0x039f => [0, 0, nil, nil, nil, 0x03bf, nil],
-
0x03a0 => [0, 0, nil, nil, nil, 0x03c0, nil],
-
0x03a1 => [0, 0, nil, nil, nil, 0x03c1, nil],
-
0x03a3 => [0, 0, nil, nil, nil, 0x03c3, nil],
-
0x03a4 => [0, 0, nil, nil, nil, 0x03c4, nil],
-
0x03a5 => [0, 0, nil, nil, nil, 0x03c5, nil],
-
0x03a6 => [0, 0, nil, nil, nil, 0x03c6, nil],
-
0x03a7 => [0, 0, nil, nil, nil, 0x03c7, nil],
-
0x03a8 => [0, 0, nil, nil, nil, 0x03c8, nil],
-
0x03a9 => [0, 0, nil, nil, nil, 0x03c9, nil],
-
0x03aa => [0, 0, "\316\231\314\210", "\316\231\314\210", nil, 0x03ca, nil],
-
0x03ab => [0, 0, "\316\245\314\210", "\316\245\314\210", nil, 0x03cb, nil],
-
0x03ac => [0, 0, "\316\261\314\201", "\316\261\314\201", 0x0386, nil, 0x0386],
-
0x03ad => [0, 0, "\316\265\314\201", "\316\265\314\201", 0x0388, nil, 0x0388],
-
0x03ae => [0, 0, "\316\267\314\201", "\316\267\314\201", 0x0389, nil, 0x0389],
-
0x03af => [0, 0, "\316\271\314\201", "\316\271\314\201", 0x038a, nil, 0x038a],
-
0x03b0 => [0, 0, "\317\213\314\201", "\317\213\314\201", nil, nil, nil],
-
0x03b1 => [0, 0, nil, nil, 0x0391, nil, 0x0391],
-
0x03b2 => [0, 0, nil, nil, 0x0392, nil, 0x0392],
-
0x03b3 => [0, 0, nil, nil, 0x0393, nil, 0x0393],
-
0x03b4 => [0, 0, nil, nil, 0x0394, nil, 0x0394],
-
0x03b5 => [0, 0, nil, nil, 0x0395, nil, 0x0395],
-
0x03b6 => [0, 0, nil, nil, 0x0396, nil, 0x0396],
-
0x03b7 => [0, 0, nil, nil, 0x0397, nil, 0x0397],
-
0x03b8 => [0, 0, nil, nil, 0x0398, nil, 0x0398],
-
0x03b9 => [0, 0, nil, nil, 0x0399, nil, 0x0399],
-
0x03ba => [0, 0, nil, nil, 0x039a, nil, 0x039a],
-
0x03bb => [0, 0, nil, nil, 0x039b, nil, 0x039b],
-
0x03bc => [0, 0, nil, nil, 0x039c, nil, 0x039c],
-
0x03bd => [0, 0, nil, nil, 0x039d, nil, 0x039d],
-
0x03be => [0, 0, nil, nil, 0x039e, nil, 0x039e],
-
0x03bf => [0, 0, nil, nil, 0x039f, nil, 0x039f],
-
0x03c0 => [0, 0, nil, nil, 0x03a0, nil, 0x03a0],
-
0x03c1 => [0, 0, nil, nil, 0x03a1, nil, 0x03a1],
-
0x03c2 => [0, 0, nil, nil, 0x03a3, nil, 0x03a3],
-
0x03c3 => [0, 0, nil, nil, 0x03a3, nil, 0x03a3],
-
0x03c4 => [0, 0, nil, nil, 0x03a4, nil, 0x03a4],
-
0x03c5 => [0, 0, nil, nil, 0x03a5, nil, 0x03a5],
-
0x03c6 => [0, 0, nil, nil, 0x03a6, nil, 0x03a6],
-
0x03c7 => [0, 0, nil, nil, 0x03a7, nil, 0x03a7],
-
0x03c8 => [0, 0, nil, nil, 0x03a8, nil, 0x03a8],
-
0x03c9 => [0, 0, nil, nil, 0x03a9, nil, 0x03a9],
-
0x03ca => [0, 0, "\316\271\314\210", "\316\271\314\210", 0x03aa, nil, 0x03aa],
-
0x03cb => [0, 0, "\317\205\314\210", "\317\205\314\210", 0x03ab, nil, 0x03ab],
-
0x03cc => [0, 0, "\316\277\314\201", "\316\277\314\201", 0x038c, nil, 0x038c],
-
0x03cd => [0, 0, "\317\205\314\201", "\317\205\314\201", 0x038e, nil, 0x038e],
-
0x03ce => [0, 0, "\317\211\314\201", "\317\211\314\201", 0x038f, nil, 0x038f],
-
0x03d0 => [0, 0, nil, "\316\262", 0x0392, nil, 0x0392],
-
0x03d1 => [0, 0, nil, "\316\270", 0x0398, nil, 0x0398],
-
0x03d2 => [0, 0, nil, "\316\245", nil, nil, nil],
-
0x03d3 => [0, 0, "\317\222\314\201", "\317\222\314\201", nil, nil, nil],
-
0x03d4 => [0, 0, "\317\222\314\210", "\317\222\314\210", nil, nil, nil],
-
0x03d5 => [0, 0, nil, "\317\206", 0x03a6, nil, 0x03a6],
-
0x03d6 => [0, 0, nil, "\317\200", 0x03a0, nil, 0x03a0],
-
0x03da => [0, 0, nil, nil, nil, 0x03db, nil],
-
0x03db => [0, 0, nil, nil, 0x03da, nil, 0x03da],
-
0x03dc => [0, 0, nil, nil, nil, 0x03dd, nil],
-
0x03dd => [0, 0, nil, nil, 0x03dc, nil, 0x03dc],
-
0x03de => [0, 0, nil, nil, nil, 0x03df, nil],
-
0x03df => [0, 0, nil, nil, 0x03de, nil, 0x03de],
-
0x03e0 => [0, 0, nil, nil, nil, 0x03e1, nil],
-
0x03e1 => [0, 0, nil, nil, 0x03e0, nil, 0x03e0],
-
0x03e2 => [0, 0, nil, nil, nil, 0x03e3, nil],
-
0x03e3 => [0, 0, nil, nil, 0x03e2, nil, 0x03e2],
-
0x03e4 => [0, 0, nil, nil, nil, 0x03e5, nil],
-
0x03e5 => [0, 0, nil, nil, 0x03e4, nil, 0x03e4],
-
0x03e6 => [0, 0, nil, nil, nil, 0x03e7, nil],
-
0x03e7 => [0, 0, nil, nil, 0x03e6, nil, 0x03e6],
-
0x03e8 => [0, 0, nil, nil, nil, 0x03e9, nil],
-
0x03e9 => [0, 0, nil, nil, 0x03e8, nil, 0x03e8],
-
0x03ea => [0, 0, nil, nil, nil, 0x03eb, nil],
-
0x03eb => [0, 0, nil, nil, 0x03ea, nil, 0x03ea],
-
0x03ec => [0, 0, nil, nil, nil, 0x03ed, nil],
-
0x03ed => [0, 0, nil, nil, 0x03ec, nil, 0x03ec],
-
0x03ee => [0, 0, nil, nil, nil, 0x03ef, nil],
-
0x03ef => [0, 0, nil, nil, 0x03ee, nil, 0x03ee],
-
0x03f0 => [0, 0, nil, "\316\272", 0x039a, nil, 0x039a],
-
0x03f1 => [0, 0, nil, "\317\201", 0x03a1, nil, 0x03a1],
-
0x03f2 => [0, 0, nil, "\317\202", 0x03a3, nil, 0x03a3],
-
0x0400 => [0, 0, "\320\225\314\200", "\320\225\314\200", nil, 0x0450, nil],
-
0x0401 => [0, 0, "\320\225\314\210", "\320\225\314\210", nil, 0x0451, nil],
-
0x0402 => [0, 0, nil, nil, nil, 0x0452, nil],
-
0x0403 => [0, 0, "\320\223\314\201", "\320\223\314\201", nil, 0x0453, nil],
-
0x0404 => [0, 0, nil, nil, nil, 0x0454, nil],
-
0x0405 => [0, 0, nil, nil, nil, 0x0455, nil],
-
0x0406 => [0, 0, nil, nil, nil, 0x0456, nil],
-
0x0407 => [0, 0, "\320\206\314\210", "\320\206\314\210", nil, 0x0457, nil],
-
0x0408 => [0, 0, nil, nil, nil, 0x0458, nil],
-
0x0409 => [0, 0, nil, nil, nil, 0x0459, nil],
-
0x040a => [0, 0, nil, nil, nil, 0x045a, nil],
-
0x040b => [0, 0, nil, nil, nil, 0x045b, nil],
-
0x040c => [0, 0, "\320\232\314\201", "\320\232\314\201", nil, 0x045c, nil],
-
0x040d => [0, 0, "\320\230\314\200", "\320\230\314\200", nil, 0x045d, nil],
-
0x040e => [0, 0, "\320\243\314\206", "\320\243\314\206", nil, 0x045e, nil],
-
0x040f => [0, 0, nil, nil, nil, 0x045f, nil],
-
0x0410 => [0, 0, nil, nil, nil, 0x0430, nil],
-
0x0411 => [0, 0, nil, nil, nil, 0x0431, nil],
-
0x0412 => [0, 0, nil, nil, nil, 0x0432, nil],
-
0x0413 => [0, 0, nil, nil, nil, 0x0433, nil],
-
0x0414 => [0, 0, nil, nil, nil, 0x0434, nil],
-
0x0415 => [0, 0, nil, nil, nil, 0x0435, nil],
-
0x0416 => [0, 0, nil, nil, nil, 0x0436, nil],
-
0x0417 => [0, 0, nil, nil, nil, 0x0437, nil],
-
0x0418 => [0, 0, nil, nil, nil, 0x0438, nil],
-
0x0419 => [0, 0, "\320\230\314\206", "\320\230\314\206", nil, 0x0439, nil],
-
0x041a => [0, 0, nil, nil, nil, 0x043a, nil],
-
0x041b => [0, 0, nil, nil, nil, 0x043b, nil],
-
0x041c => [0, 0, nil, nil, nil, 0x043c, nil],
-
0x041d => [0, 0, nil, nil, nil, 0x043d, nil],
-
0x041e => [0, 0, nil, nil, nil, 0x043e, nil],
-
0x041f => [0, 0, nil, nil, nil, 0x043f, nil],
-
0x0420 => [0, 0, nil, nil, nil, 0x0440, nil],
-
0x0421 => [0, 0, nil, nil, nil, 0x0441, nil],
-
0x0422 => [0, 0, nil, nil, nil, 0x0442, nil],
-
0x0423 => [0, 0, nil, nil, nil, 0x0443, nil],
-
0x0424 => [0, 0, nil, nil, nil, 0x0444, nil],
-
0x0425 => [0, 0, nil, nil, nil, 0x0445, nil],
-
0x0426 => [0, 0, nil, nil, nil, 0x0446, nil],
-
0x0427 => [0, 0, nil, nil, nil, 0x0447, nil],
-
0x0428 => [0, 0, nil, nil, nil, 0x0448, nil],
-
0x0429 => [0, 0, nil, nil, nil, 0x0449, nil],
-
0x042a => [0, 0, nil, nil, nil, 0x044a, nil],
-
0x042b => [0, 0, nil, nil, nil, 0x044b, nil],
-
0x042c => [0, 0, nil, nil, nil, 0x044c, nil],
-
0x042d => [0, 0, nil, nil, nil, 0x044d, nil],
-
0x042e => [0, 0, nil, nil, nil, 0x044e, nil],
-
0x042f => [0, 0, nil, nil, nil, 0x044f, nil],
-
0x0430 => [0, 0, nil, nil, 0x0410, nil, 0x0410],
-
0x0431 => [0, 0, nil, nil, 0x0411, nil, 0x0411],
-
0x0432 => [0, 0, nil, nil, 0x0412, nil, 0x0412],
-
0x0433 => [0, 0, nil, nil, 0x0413, nil, 0x0413],
-
0x0434 => [0, 0, nil, nil, 0x0414, nil, 0x0414],
-
0x0435 => [0, 0, nil, nil, 0x0415, nil, 0x0415],
-
0x0436 => [0, 0, nil, nil, 0x0416, nil, 0x0416],
-
0x0437 => [0, 0, nil, nil, 0x0417, nil, 0x0417],
-
0x0438 => [0, 0, nil, nil, 0x0418, nil, 0x0418],
-
0x0439 => [0, 0, "\320\270\314\206", "\320\270\314\206", 0x0419, nil, 0x0419],
-
0x043a => [0, 0, nil, nil, 0x041a, nil, 0x041a],
-
0x043b => [0, 0, nil, nil, 0x041b, nil, 0x041b],
-
0x043c => [0, 0, nil, nil, 0x041c, nil, 0x041c],
-
0x043d => [0, 0, nil, nil, 0x041d, nil, 0x041d],
-
0x043e => [0, 0, nil, nil, 0x041e, nil, 0x041e],
-
0x043f => [0, 0, nil, nil, 0x041f, nil, 0x041f],
-
0x0440 => [0, 0, nil, nil, 0x0420, nil, 0x0420],
-
0x0441 => [0, 0, nil, nil, 0x0421, nil, 0x0421],
-
0x0442 => [0, 0, nil, nil, 0x0422, nil, 0x0422],
-
0x0443 => [0, 0, nil, nil, 0x0423, nil, 0x0423],
-
0x0444 => [0, 0, nil, nil, 0x0424, nil, 0x0424],
-
0x0445 => [0, 0, nil, nil, 0x0425, nil, 0x0425],
-
0x0446 => [0, 0, nil, nil, 0x0426, nil, 0x0426],
-
0x0447 => [0, 0, nil, nil, 0x0427, nil, 0x0427],
-
0x0448 => [0, 0, nil, nil, 0x0428, nil, 0x0428],
-
0x0449 => [0, 0, nil, nil, 0x0429, nil, 0x0429],
-
0x044a => [0, 0, nil, nil, 0x042a, nil, 0x042a],
-
0x044b => [0, 0, nil, nil, 0x042b, nil, 0x042b],
-
0x044c => [0, 0, nil, nil, 0x042c, nil, 0x042c],
-
0x044d => [0, 0, nil, nil, 0x042d, nil, 0x042d],
-
0x044e => [0, 0, nil, nil, 0x042e, nil, 0x042e],
-
0x044f => [0, 0, nil, nil, 0x042f, nil, 0x042f],
-
0x0450 => [0, 0, "\320\265\314\200", "\320\265\314\200", 0x0400, nil, 0x0400],
-
0x0451 => [0, 0, "\320\265\314\210", "\320\265\314\210", 0x0401, nil, 0x0401],
-
0x0452 => [0, 0, nil, nil, 0x0402, nil, 0x0402],
-
0x0453 => [0, 0, "\320\263\314\201", "\320\263\314\201", 0x0403, nil, 0x0403],
-
0x0454 => [0, 0, nil, nil, 0x0404, nil, 0x0404],
-
0x0455 => [0, 0, nil, nil, 0x0405, nil, 0x0405],
-
0x0456 => [0, 0, nil, nil, 0x0406, nil, 0x0406],
-
0x0457 => [0, 0, "\321\226\314\210", "\321\226\314\210", 0x0407, nil, 0x0407],
-
0x0458 => [0, 0, nil, nil, 0x0408, nil, 0x0408],
-
0x0459 => [0, 0, nil, nil, 0x0409, nil, 0x0409],
-
0x045a => [0, 0, nil, nil, 0x040a, nil, 0x040a],
-
0x045b => [0, 0, nil, nil, 0x040b, nil, 0x040b],
-
0x045c => [0, 0, "\320\272\314\201", "\320\272\314\201", 0x040c, nil, 0x040c],
-
0x045d => [0, 0, "\320\270\314\200", "\320\270\314\200", 0x040d, nil, 0x040d],
-
0x045e => [0, 0, "\321\203\314\206", "\321\203\314\206", 0x040e, nil, 0x040e],
-
0x045f => [0, 0, nil, nil, 0x040f, nil, 0x040f],
-
0x0460 => [0, 0, nil, nil, nil, 0x0461, nil],
-
0x0461 => [0, 0, nil, nil, 0x0460, nil, 0x0460],
-
0x0462 => [0, 0, nil, nil, nil, 0x0463, nil],
-
0x0463 => [0, 0, nil, nil, 0x0462, nil, 0x0462],
-
0x0464 => [0, 0, nil, nil, nil, 0x0465, nil],
-
0x0465 => [0, 0, nil, nil, 0x0464, nil, 0x0464],
-
0x0466 => [0, 0, nil, nil, nil, 0x0467, nil],
-
0x0467 => [0, 0, nil, nil, 0x0466, nil, 0x0466],
-
0x0468 => [0, 0, nil, nil, nil, 0x0469, nil],
-
0x0469 => [0, 0, nil, nil, 0x0468, nil, 0x0468],
-
0x046a => [0, 0, nil, nil, nil, 0x046b, nil],
-
0x046b => [0, 0, nil, nil, 0x046a, nil, 0x046a],
-
0x046c => [0, 0, nil, nil, nil, 0x046d, nil],
-
0x046d => [0, 0, nil, nil, 0x046c, nil, 0x046c],
-
0x046e => [0, 0, nil, nil, nil, 0x046f, nil],
-
0x046f => [0, 0, nil, nil, 0x046e, nil, 0x046e],
-
0x0470 => [0, 0, nil, nil, nil, 0x0471, nil],
-
0x0471 => [0, 0, nil, nil, 0x0470, nil, 0x0470],
-
0x0472 => [0, 0, nil, nil, nil, 0x0473, nil],
-
0x0473 => [0, 0, nil, nil, 0x0472, nil, 0x0472],
-
0x0474 => [0, 0, nil, nil, nil, 0x0475, nil],
-
0x0475 => [0, 0, nil, nil, 0x0474, nil, 0x0474],
-
0x0476 => [0, 0, "\321\264\314\217", "\321\264\314\217", nil, 0x0477, nil],
-
0x0477 => [0, 0, "\321\265\314\217", "\321\265\314\217", 0x0476, nil, 0x0476],
-
0x0478 => [0, 0, nil, nil, nil, 0x0479, nil],
-
0x0479 => [0, 0, nil, nil, 0x0478, nil, 0x0478],
-
0x047a => [0, 0, nil, nil, nil, 0x047b, nil],
-
0x047b => [0, 0, nil, nil, 0x047a, nil, 0x047a],
-
0x047c => [0, 0, nil, nil, nil, 0x047d, nil],
-
0x047d => [0, 0, nil, nil, 0x047c, nil, 0x047c],
-
0x047e => [0, 0, nil, nil, nil, 0x047f, nil],
-
0x047f => [0, 0, nil, nil, 0x047e, nil, 0x047e],
-
0x0480 => [0, 0, nil, nil, nil, 0x0481, nil],
-
0x0481 => [0, 0, nil, nil, 0x0480, nil, 0x0480],
-
0x0483 => [230, 0, nil, nil, nil, nil, nil],
-
0x0484 => [230, 0, nil, nil, nil, nil, nil],
-
0x0485 => [230, 0, nil, nil, nil, nil, nil],
-
0x0486 => [230, 0, nil, nil, nil, nil, nil],
-
0x048c => [0, 0, nil, nil, nil, 0x048d, nil],
-
0x048d => [0, 0, nil, nil, 0x048c, nil, 0x048c],
-
0x048e => [0, 0, nil, nil, nil, 0x048f, nil],
-
0x048f => [0, 0, nil, nil, 0x048e, nil, 0x048e],
-
0x0490 => [0, 0, nil, nil, nil, 0x0491, nil],
-
0x0491 => [0, 0, nil, nil, 0x0490, nil, 0x0490],
-
0x0492 => [0, 0, nil, nil, nil, 0x0493, nil],
-
0x0493 => [0, 0, nil, nil, 0x0492, nil, 0x0492],
-
0x0494 => [0, 0, nil, nil, nil, 0x0495, nil],
-
0x0495 => [0, 0, nil, nil, 0x0494, nil, 0x0494],
-
0x0496 => [0, 0, nil, nil, nil, 0x0497, nil],
-
0x0497 => [0, 0, nil, nil, 0x0496, nil, 0x0496],
-
0x0498 => [0, 0, nil, nil, nil, 0x0499, nil],
-
0x0499 => [0, 0, nil, nil, 0x0498, nil, 0x0498],
-
0x049a => [0, 0, nil, nil, nil, 0x049b, nil],
-
0x049b => [0, 0, nil, nil, 0x049a, nil, 0x049a],
-
0x049c => [0, 0, nil, nil, nil, 0x049d, nil],
-
0x049d => [0, 0, nil, nil, 0x049c, nil, 0x049c],
-
0x049e => [0, 0, nil, nil, nil, 0x049f, nil],
-
0x049f => [0, 0, nil, nil, 0x049e, nil, 0x049e],
-
0x04a0 => [0, 0, nil, nil, nil, 0x04a1, nil],
-
0x04a1 => [0, 0, nil, nil, 0x04a0, nil, 0x04a0],
-
0x04a2 => [0, 0, nil, nil, nil, 0x04a3, nil],
-
0x04a3 => [0, 0, nil, nil, 0x04a2, nil, 0x04a2],
-
0x04a4 => [0, 0, nil, nil, nil, 0x04a5, nil],
-
0x04a5 => [0, 0, nil, nil, 0x04a4, nil, 0x04a4],
-
0x04a6 => [0, 0, nil, nil, nil, 0x04a7, nil],
-
0x04a7 => [0, 0, nil, nil, 0x04a6, nil, 0x04a6],
-
0x04a8 => [0, 0, nil, nil, nil, 0x04a9, nil],
-
0x04a9 => [0, 0, nil, nil, 0x04a8, nil, 0x04a8],
-
0x04aa => [0, 0, nil, nil, nil, 0x04ab, nil],
-
0x04ab => [0, 0, nil, nil, 0x04aa, nil, 0x04aa],
-
0x04ac => [0, 0, nil, nil, nil, 0x04ad, nil],
-
0x04ad => [0, 0, nil, nil, 0x04ac, nil, 0x04ac],
-
0x04ae => [0, 0, nil, nil, nil, 0x04af, nil],
-
0x04af => [0, 0, nil, nil, 0x04ae, nil, 0x04ae],
-
0x04b0 => [0, 0, nil, nil, nil, 0x04b1, nil],
-
0x04b1 => [0, 0, nil, nil, 0x04b0, nil, 0x04b0],
-
0x04b2 => [0, 0, nil, nil, nil, 0x04b3, nil],
-
0x04b3 => [0, 0, nil, nil, 0x04b2, nil, 0x04b2],
-
0x04b4 => [0, 0, nil, nil, nil, 0x04b5, nil],
-
0x04b5 => [0, 0, nil, nil, 0x04b4, nil, 0x04b4],
-
0x04b6 => [0, 0, nil, nil, nil, 0x04b7, nil],
-
0x04b7 => [0, 0, nil, nil, 0x04b6, nil, 0x04b6],
-
0x04b8 => [0, 0, nil, nil, nil, 0x04b9, nil],
-
0x04b9 => [0, 0, nil, nil, 0x04b8, nil, 0x04b8],
-
0x04ba => [0, 0, nil, nil, nil, 0x04bb, nil],
-
0x04bb => [0, 0, nil, nil, 0x04ba, nil, 0x04ba],
-
0x04bc => [0, 0, nil, nil, nil, 0x04bd, nil],
-
0x04bd => [0, 0, nil, nil, 0x04bc, nil, 0x04bc],
-
0x04be => [0, 0, nil, nil, nil, 0x04bf, nil],
-
0x04bf => [0, 0, nil, nil, 0x04be, nil, 0x04be],
-
0x04c1 => [0, 0, "\320\226\314\206", "\320\226\314\206", nil, 0x04c2, nil],
-
0x04c2 => [0, 0, "\320\266\314\206", "\320\266\314\206", 0x04c1, nil, 0x04c1],
-
0x04c3 => [0, 0, nil, nil, nil, 0x04c4, nil],
-
0x04c4 => [0, 0, nil, nil, 0x04c3, nil, 0x04c3],
-
0x04c7 => [0, 0, nil, nil, nil, 0x04c8, nil],
-
0x04c8 => [0, 0, nil, nil, 0x04c7, nil, 0x04c7],
-
0x04cb => [0, 0, nil, nil, nil, 0x04cc, nil],
-
0x04cc => [0, 0, nil, nil, 0x04cb, nil, 0x04cb],
-
0x04d0 => [0, 0, "\320\220\314\206", "\320\220\314\206", nil, 0x04d1, nil],
-
0x04d1 => [0, 0, "\320\260\314\206", "\320\260\314\206", 0x04d0, nil, 0x04d0],
-
0x04d2 => [0, 0, "\320\220\314\210", "\320\220\314\210", nil, 0x04d3, nil],
-
0x04d3 => [0, 0, "\320\260\314\210", "\320\260\314\210", 0x04d2, nil, 0x04d2],
-
0x04d4 => [0, 0, nil, nil, nil, 0x04d5, nil],
-
0x04d5 => [0, 0, nil, nil, 0x04d4, nil, 0x04d4],
-
0x04d6 => [0, 0, "\320\225\314\206", "\320\225\314\206", nil, 0x04d7, nil],
-
0x04d7 => [0, 0, "\320\265\314\206", "\320\265\314\206", 0x04d6, nil, 0x04d6],
-
0x04d8 => [0, 0, nil, nil, nil, 0x04d9, nil],
-
0x04d9 => [0, 0, nil, nil, 0x04d8, nil, 0x04d8],
-
0x04da => [0, 0, "\323\230\314\210", "\323\230\314\210", nil, 0x04db, nil],
-
0x04db => [0, 0, "\323\231\314\210", "\323\231\314\210", 0x04da, nil, 0x04da],
-
0x04dc => [0, 0, "\320\226\314\210", "\320\226\314\210", nil, 0x04dd, nil],
-
0x04dd => [0, 0, "\320\266\314\210", "\320\266\314\210", 0x04dc, nil, 0x04dc],
-
0x04de => [0, 0, "\320\227\314\210", "\320\227\314\210", nil, 0x04df, nil],
-
0x04df => [0, 0, "\320\267\314\210", "\320\267\314\210", 0x04de, nil, 0x04de],
-
0x04e0 => [0, 0, nil, nil, nil, 0x04e1, nil],
-
0x04e1 => [0, 0, nil, nil, 0x04e0, nil, 0x04e0],
-
0x04e2 => [0, 0, "\320\230\314\204", "\320\230\314\204", nil, 0x04e3, nil],
-
0x04e3 => [0, 0, "\320\270\314\204", "\320\270\314\204", 0x04e2, nil, 0x04e2],
-
0x04e4 => [0, 0, "\320\230\314\210", "\320\230\314\210", nil, 0x04e5, nil],
-
0x04e5 => [0, 0, "\320\270\314\210", "\320\270\314\210", 0x04e4, nil, 0x04e4],
-
0x04e6 => [0, 0, "\320\236\314\210", "\320\236\314\210", nil, 0x04e7, nil],
-
0x04e7 => [0, 0, "\320\276\314\210", "\320\276\314\210", 0x04e6, nil, 0x04e6],
-
0x04e8 => [0, 0, nil, nil, nil, 0x04e9, nil],
-
0x04e9 => [0, 0, nil, nil, 0x04e8, nil, 0x04e8],
-
0x04ea => [0, 0, "\323\250\314\210", "\323\250\314\210", nil, 0x04eb, nil],
-
0x04eb => [0, 0, "\323\251\314\210", "\323\251\314\210", 0x04ea, nil, 0x04ea],
-
0x04ec => [0, 0, "\320\255\314\210", "\320\255\314\210", nil, 0x04ed, nil],
-
0x04ed => [0, 0, "\321\215\314\210", "\321\215\314\210", 0x04ec, nil, 0x04ec],
-
0x04ee => [0, 0, "\320\243\314\204", "\320\243\314\204", nil, 0x04ef, nil],
-
0x04ef => [0, 0, "\321\203\314\204", "\321\203\314\204", 0x04ee, nil, 0x04ee],
-
0x04f0 => [0, 0, "\320\243\314\210", "\320\243\314\210", nil, 0x04f1, nil],
-
0x04f1 => [0, 0, "\321\203\314\210", "\321\203\314\210", 0x04f0, nil, 0x04f0],
-
0x04f2 => [0, 0, "\320\243\314\213", "\320\243\314\213", nil, 0x04f3, nil],
-
0x04f3 => [0, 0, "\321\203\314\213", "\321\203\314\213", 0x04f2, nil, 0x04f2],
-
0x04f4 => [0, 0, "\320\247\314\210", "\320\247\314\210", nil, 0x04f5, nil],
-
0x04f5 => [0, 0, "\321\207\314\210", "\321\207\314\210", 0x04f4, nil, 0x04f4],
-
0x04f8 => [0, 0, "\320\253\314\210", "\320\253\314\210", nil, 0x04f9, nil],
-
0x04f9 => [0, 0, "\321\213\314\210", "\321\213\314\210", 0x04f8, nil, 0x04f8],
-
0x0531 => [0, 0, nil, nil, nil, 0x0561, nil],
-
0x0532 => [0, 0, nil, nil, nil, 0x0562, nil],
-
0x0533 => [0, 0, nil, nil, nil, 0x0563, nil],
-
0x0534 => [0, 0, nil, nil, nil, 0x0564, nil],
-
0x0535 => [0, 0, nil, nil, nil, 0x0565, nil],
-
0x0536 => [0, 0, nil, nil, nil, 0x0566, nil],
-
0x0537 => [0, 0, nil, nil, nil, 0x0567, nil],
-
0x0538 => [0, 0, nil, nil, nil, 0x0568, nil],
-
0x0539 => [0, 0, nil, nil, nil, 0x0569, nil],
-
0x053a => [0, 0, nil, nil, nil, 0x056a, nil],
-
0x053b => [0, 0, nil, nil, nil, 0x056b, nil],
-
0x053c => [0, 0, nil, nil, nil, 0x056c, nil],
-
0x053d => [0, 0, nil, nil, nil, 0x056d, nil],
-
0x053e => [0, 0, nil, nil, nil, 0x056e, nil],
-
0x053f => [0, 0, nil, nil, nil, 0x056f, nil],
-
0x0540 => [0, 0, nil, nil, nil, 0x0570, nil],
-
0x0541 => [0, 0, nil, nil, nil, 0x0571, nil],
-
0x0542 => [0, 0, nil, nil, nil, 0x0572, nil],
-
0x0543 => [0, 0, nil, nil, nil, 0x0573, nil],
-
0x0544 => [0, 0, nil, nil, nil, 0x0574, nil],
-
0x0545 => [0, 0, nil, nil, nil, 0x0575, nil],
-
0x0546 => [0, 0, nil, nil, nil, 0x0576, nil],
-
0x0547 => [0, 0, nil, nil, nil, 0x0577, nil],
-
0x0548 => [0, 0, nil, nil, nil, 0x0578, nil],
-
0x0549 => [0, 0, nil, nil, nil, 0x0579, nil],
-
0x054a => [0, 0, nil, nil, nil, 0x057a, nil],
-
0x054b => [0, 0, nil, nil, nil, 0x057b, nil],
-
0x054c => [0, 0, nil, nil, nil, 0x057c, nil],
-
0x054d => [0, 0, nil, nil, nil, 0x057d, nil],
-
0x054e => [0, 0, nil, nil, nil, 0x057e, nil],
-
0x054f => [0, 0, nil, nil, nil, 0x057f, nil],
-
0x0550 => [0, 0, nil, nil, nil, 0x0580, nil],
-
0x0551 => [0, 0, nil, nil, nil, 0x0581, nil],
-
0x0552 => [0, 0, nil, nil, nil, 0x0582, nil],
-
0x0553 => [0, 0, nil, nil, nil, 0x0583, nil],
-
0x0554 => [0, 0, nil, nil, nil, 0x0584, nil],
-
0x0555 => [0, 0, nil, nil, nil, 0x0585, nil],
-
0x0556 => [0, 0, nil, nil, nil, 0x0586, nil],
-
0x0561 => [0, 0, nil, nil, 0x0531, nil, 0x0531],
-
0x0562 => [0, 0, nil, nil, 0x0532, nil, 0x0532],
-
0x0563 => [0, 0, nil, nil, 0x0533, nil, 0x0533],
-
0x0564 => [0, 0, nil, nil, 0x0534, nil, 0x0534],
-
0x0565 => [0, 0, nil, nil, 0x0535, nil, 0x0535],
-
0x0566 => [0, 0, nil, nil, 0x0536, nil, 0x0536],
-
0x0567 => [0, 0, nil, nil, 0x0537, nil, 0x0537],
-
0x0568 => [0, 0, nil, nil, 0x0538, nil, 0x0538],
-
0x0569 => [0, 0, nil, nil, 0x0539, nil, 0x0539],
-
0x056a => [0, 0, nil, nil, 0x053a, nil, 0x053a],
-
0x056b => [0, 0, nil, nil, 0x053b, nil, 0x053b],
-
0x056c => [0, 0, nil, nil, 0x053c, nil, 0x053c],
-
0x056d => [0, 0, nil, nil, 0x053d, nil, 0x053d],
-
0x056e => [0, 0, nil, nil, 0x053e, nil, 0x053e],
-
0x056f => [0, 0, nil, nil, 0x053f, nil, 0x053f],
-
0x0570 => [0, 0, nil, nil, 0x0540, nil, 0x0540],
-
0x0571 => [0, 0, nil, nil, 0x0541, nil, 0x0541],
-
0x0572 => [0, 0, nil, nil, 0x0542, nil, 0x0542],
-
0x0573 => [0, 0, nil, nil, 0x0543, nil, 0x0543],
-
0x0574 => [0, 0, nil, nil, 0x0544, nil, 0x0544],
-
0x0575 => [0, 0, nil, nil, 0x0545, nil, 0x0545],
-
0x0576 => [0, 0, nil, nil, 0x0546, nil, 0x0546],
-
0x0577 => [0, 0, nil, nil, 0x0547, nil, 0x0547],
-
0x0578 => [0, 0, nil, nil, 0x0548, nil, 0x0548],
-
0x0579 => [0, 0, nil, nil, 0x0549, nil, 0x0549],
-
0x057a => [0, 0, nil, nil, 0x054a, nil, 0x054a],
-
0x057b => [0, 0, nil, nil, 0x054b, nil, 0x054b],
-
0x057c => [0, 0, nil, nil, 0x054c, nil, 0x054c],
-
0x057d => [0, 0, nil, nil, 0x054d, nil, 0x054d],
-
0x057e => [0, 0, nil, nil, 0x054e, nil, 0x054e],
-
0x057f => [0, 0, nil, nil, 0x054f, nil, 0x054f],
-
0x0580 => [0, 0, nil, nil, 0x0550, nil, 0x0550],
-
0x0581 => [0, 0, nil, nil, 0x0551, nil, 0x0551],
-
0x0582 => [0, 0, nil, nil, 0x0552, nil, 0x0552],
-
0x0583 => [0, 0, nil, nil, 0x0553, nil, 0x0553],
-
0x0584 => [0, 0, nil, nil, 0x0554, nil, 0x0554],
-
0x0585 => [0, 0, nil, nil, 0x0555, nil, 0x0555],
-
0x0586 => [0, 0, nil, nil, 0x0556, nil, 0x0556],
-
0x0587 => [0, 0, nil, "\325\245\326\202", nil, nil, nil],
-
0x0591 => [220, 0, nil, nil, nil, nil, nil],
-
0x0592 => [230, 0, nil, nil, nil, nil, nil],
-
0x0593 => [230, 0, nil, nil, nil, nil, nil],
-
0x0594 => [230, 0, nil, nil, nil, nil, nil],
-
0x0595 => [230, 0, nil, nil, nil, nil, nil],
-
0x0596 => [220, 0, nil, nil, nil, nil, nil],
-
0x0597 => [230, 0, nil, nil, nil, nil, nil],
-
0x0598 => [230, 0, nil, nil, nil, nil, nil],
-
0x0599 => [230, 0, nil, nil, nil, nil, nil],
-
0x059a => [222, 0, nil, nil, nil, nil, nil],
-
0x059b => [220, 0, nil, nil, nil, nil, nil],
-
0x059c => [230, 0, nil, nil, nil, nil, nil],
-
0x059d => [230, 0, nil, nil, nil, nil, nil],
-
0x059e => [230, 0, nil, nil, nil, nil, nil],
-
0x059f => [230, 0, nil, nil, nil, nil, nil],
-
0x05a0 => [230, 0, nil, nil, nil, nil, nil],
-
0x05a1 => [230, 0, nil, nil, nil, nil, nil],
-
0x05a3 => [220, 0, nil, nil, nil, nil, nil],
-
0x05a4 => [220, 0, nil, nil, nil, nil, nil],
-
0x05a5 => [220, 0, nil, nil, nil, nil, nil],
-
0x05a6 => [220, 0, nil, nil, nil, nil, nil],
-
0x05a7 => [220, 0, nil, nil, nil, nil, nil],
-
0x05a8 => [230, 0, nil, nil, nil, nil, nil],
-
0x05a9 => [230, 0, nil, nil, nil, nil, nil],
-
0x05aa => [220, 0, nil, nil, nil, nil, nil],
-
0x05ab => [230, 0, nil, nil, nil, nil, nil],
-
0x05ac => [230, 0, nil, nil, nil, nil, nil],
-
0x05ad => [222, 0, nil, nil, nil, nil, nil],
-
0x05ae => [228, 0, nil, nil, nil, nil, nil],
-
0x05af => [230, 0, nil, nil, nil, nil, nil],
-
0x05b0 => [10, 0, nil, nil, nil, nil, nil],
-
0x05b1 => [11, 0, nil, nil, nil, nil, nil],
-
0x05b2 => [12, 0, nil, nil, nil, nil, nil],
-
0x05b3 => [13, 0, nil, nil, nil, nil, nil],
-
0x05b4 => [14, 0, nil, nil, nil, nil, nil],
-
0x05b5 => [15, 0, nil, nil, nil, nil, nil],
-
0x05b6 => [16, 0, nil, nil, nil, nil, nil],
-
0x05b7 => [17, 0, nil, nil, nil, nil, nil],
-
0x05b8 => [18, 0, nil, nil, nil, nil, nil],
-
0x05b9 => [19, 0, nil, nil, nil, nil, nil],
-
0x05bb => [20, 0, nil, nil, nil, nil, nil],
-
0x05bc => [21, 0, nil, nil, nil, nil, nil],
-
0x05bd => [22, 0, nil, nil, nil, nil, nil],
-
0x05bf => [23, 0, nil, nil, nil, nil, nil],
-
0x05c1 => [24, 0, nil, nil, nil, nil, nil],
-
0x05c2 => [25, 0, nil, nil, nil, nil, nil],
-
0x05c4 => [230, 0, nil, nil, nil, nil, nil],
-
0x0622 => [0, 0, "\330\247\331\223", "\330\247\331\223", nil, nil, nil],
-
0x0623 => [0, 0, "\330\247\331\224", "\330\247\331\224", nil, nil, nil],
-
0x0624 => [0, 0, "\331\210\331\224", "\331\210\331\224", nil, nil, nil],
-
0x0625 => [0, 0, "\330\247\331\225", "\330\247\331\225", nil, nil, nil],
-
0x0626 => [0, 0, "\331\212\331\224", "\331\212\331\224", nil, nil, nil],
-
0x064b => [27, 0, nil, nil, nil, nil, nil],
-
0x064c => [28, 0, nil, nil, nil, nil, nil],
-
0x064d => [29, 0, nil, nil, nil, nil, nil],
-
0x064e => [30, 0, nil, nil, nil, nil, nil],
-
0x064f => [31, 0, nil, nil, nil, nil, nil],
-
0x0650 => [32, 0, nil, nil, nil, nil, nil],
-
0x0651 => [33, 0, nil, nil, nil, nil, nil],
-
0x0652 => [34, 0, nil, nil, nil, nil, nil],
-
0x0653 => [230, 0, nil, nil, nil, nil, nil],
-
0x0654 => [230, 0, nil, nil, nil, nil, nil],
-
0x0655 => [220, 0, nil, nil, nil, nil, nil],
-
0x0670 => [35, 0, nil, nil, nil, nil, nil],
-
0x0675 => [0, 0, nil, "\330\247\331\264", nil, nil, nil],
-
0x0676 => [0, 0, nil, "\331\210\331\264", nil, nil, nil],
-
0x0677 => [0, 0, nil, "\333\207\331\264", nil, nil, nil],
-
0x0678 => [0, 0, nil, "\331\212\331\264", nil, nil, nil],
-
0x06c0 => [0, 0, "\333\225\331\224", "\333\225\331\224", nil, nil, nil],
-
0x06c2 => [0, 0, "\333\201\331\224", "\333\201\331\224", nil, nil, nil],
-
0x06d3 => [0, 0, "\333\222\331\224", "\333\222\331\224", nil, nil, nil],
-
0x06d6 => [230, 0, nil, nil, nil, nil, nil],
-
0x06d7 => [230, 0, nil, nil, nil, nil, nil],
-
0x06d8 => [230, 0, nil, nil, nil, nil, nil],
-
0x06d9 => [230, 0, nil, nil, nil, nil, nil],
-
0x06da => [230, 0, nil, nil, nil, nil, nil],
-
0x06db => [230, 0, nil, nil, nil, nil, nil],
-
0x06dc => [230, 0, nil, nil, nil, nil, nil],
-
0x06df => [230, 0, nil, nil, nil, nil, nil],
-
0x06e0 => [230, 0, nil, nil, nil, nil, nil],
-
0x06e1 => [230, 0, nil, nil, nil, nil, nil],
-
0x06e2 => [230, 0, nil, nil, nil, nil, nil],
-
0x06e3 => [220, 0, nil, nil, nil, nil, nil],
-
0x06e4 => [230, 0, nil, nil, nil, nil, nil],
-
0x06e7 => [230, 0, nil, nil, nil, nil, nil],
-
0x06e8 => [230, 0, nil, nil, nil, nil, nil],
-
0x06ea => [220, 0, nil, nil, nil, nil, nil],
-
0x06eb => [230, 0, nil, nil, nil, nil, nil],
-
0x06ec => [230, 0, nil, nil, nil, nil, nil],
-
0x06ed => [220, 0, nil, nil, nil, nil, nil],
-
0x0711 => [36, 0, nil, nil, nil, nil, nil],
-
0x0730 => [230, 0, nil, nil, nil, nil, nil],
-
0x0731 => [220, 0, nil, nil, nil, nil, nil],
-
0x0732 => [230, 0, nil, nil, nil, nil, nil],
-
0x0733 => [230, 0, nil, nil, nil, nil, nil],
-
0x0734 => [220, 0, nil, nil, nil, nil, nil],
-
0x0735 => [230, 0, nil, nil, nil, nil, nil],
-
0x0736 => [230, 0, nil, nil, nil, nil, nil],
-
0x0737 => [220, 0, nil, nil, nil, nil, nil],
-
0x0738 => [220, 0, nil, nil, nil, nil, nil],
-
0x0739 => [220, 0, nil, nil, nil, nil, nil],
-
0x073a => [230, 0, nil, nil, nil, nil, nil],
-
0x073b => [220, 0, nil, nil, nil, nil, nil],
-
0x073c => [220, 0, nil, nil, nil, nil, nil],
-
0x073d => [230, 0, nil, nil, nil, nil, nil],
-
0x073e => [220, 0, nil, nil, nil, nil, nil],
-
0x073f => [230, 0, nil, nil, nil, nil, nil],
-
0x0740 => [230, 0, nil, nil, nil, nil, nil],
-
0x0741 => [230, 0, nil, nil, nil, nil, nil],
-
0x0742 => [220, 0, nil, nil, nil, nil, nil],
-
0x0743 => [230, 0, nil, nil, nil, nil, nil],
-
0x0744 => [220, 0, nil, nil, nil, nil, nil],
-
0x0745 => [230, 0, nil, nil, nil, nil, nil],
-
0x0746 => [220, 0, nil, nil, nil, nil, nil],
-
0x0747 => [230, 0, nil, nil, nil, nil, nil],
-
0x0748 => [220, 0, nil, nil, nil, nil, nil],
-
0x0749 => [230, 0, nil, nil, nil, nil, nil],
-
0x074a => [230, 0, nil, nil, nil, nil, nil],
-
0x0929 => [0, 0, "\340\244\250\340\244\274", "\340\244\250\340\244\274", nil, nil, nil],
-
0x0931 => [0, 0, "\340\244\260\340\244\274", "\340\244\260\340\244\274", nil, nil, nil],
-
0x0934 => [0, 0, "\340\244\263\340\244\274", "\340\244\263\340\244\274", nil, nil, nil],
-
0x093c => [7, 0, nil, nil, nil, nil, nil],
-
0x094d => [9, 0, nil, nil, nil, nil, nil],
-
0x0951 => [230, 0, nil, nil, nil, nil, nil],
-
0x0952 => [220, 0, nil, nil, nil, nil, nil],
-
0x0953 => [230, 0, nil, nil, nil, nil, nil],
-
0x0954 => [230, 0, nil, nil, nil, nil, nil],
-
0x0958 => [0, 1, "\340\244\225\340\244\274", "\340\244\225\340\244\274", nil, nil, nil],
-
0x0959 => [0, 1, "\340\244\226\340\244\274", "\340\244\226\340\244\274", nil, nil, nil],
-
0x095a => [0, 1, "\340\244\227\340\244\274", "\340\244\227\340\244\274", nil, nil, nil],
-
0x095b => [0, 1, "\340\244\234\340\244\274", "\340\244\234\340\244\274", nil, nil, nil],
-
0x095c => [0, 1, "\340\244\241\340\244\274", "\340\244\241\340\244\274", nil, nil, nil],
-
0x095d => [0, 1, "\340\244\242\340\244\274", "\340\244\242\340\244\274", nil, nil, nil],
-
0x095e => [0, 1, "\340\244\253\340\244\274", "\340\244\253\340\244\274", nil, nil, nil],
-
0x095f => [0, 1, "\340\244\257\340\244\274", "\340\244\257\340\244\274", nil, nil, nil],
-
0x09bc => [7, 0, nil, nil, nil, nil, nil],
-
0x09cb => [0, 0, "\340\247\207\340\246\276", "\340\247\207\340\246\276", nil, nil, nil],
-
0x09cc => [0, 0, "\340\247\207\340\247\227", "\340\247\207\340\247\227", nil, nil, nil],
-
0x09cd => [9, 0, nil, nil, nil, nil, nil],
-
0x09dc => [0, 1, "\340\246\241\340\246\274", "\340\246\241\340\246\274", nil, nil, nil],
-
0x09dd => [0, 1, "\340\246\242\340\246\274", "\340\246\242\340\246\274", nil, nil, nil],
-
0x09df => [0, 1, "\340\246\257\340\246\274", "\340\246\257\340\246\274", nil, nil, nil],
-
0x0a33 => [0, 1, "\340\250\262\340\250\274", "\340\250\262\340\250\274", nil, nil, nil],
-
0x0a36 => [0, 1, "\340\250\270\340\250\274", "\340\250\270\340\250\274", nil, nil, nil],
-
0x0a3c => [7, 0, nil, nil, nil, nil, nil],
-
0x0a4d => [9, 0, nil, nil, nil, nil, nil],
-
0x0a59 => [0, 1, "\340\250\226\340\250\274", "\340\250\226\340\250\274", nil, nil, nil],
-
0x0a5a => [0, 1, "\340\250\227\340\250\274", "\340\250\227\340\250\274", nil, nil, nil],
-
0x0a5b => [0, 1, "\340\250\234\340\250\274", "\340\250\234\340\250\274", nil, nil, nil],
-
0x0a5e => [0, 1, "\340\250\253\340\250\274", "\340\250\253\340\250\274", nil, nil, nil],
-
0x0abc => [7, 0, nil, nil, nil, nil, nil],
-
0x0acd => [9, 0, nil, nil, nil, nil, nil],
-
0x0b3c => [7, 0, nil, nil, nil, nil, nil],
-
0x0b48 => [0, 0, "\340\255\207\340\255\226", "\340\255\207\340\255\226", nil, nil, nil],
-
0x0b4b => [0, 0, "\340\255\207\340\254\276", "\340\255\207\340\254\276", nil, nil, nil],
-
0x0b4c => [0, 0, "\340\255\207\340\255\227", "\340\255\207\340\255\227", nil, nil, nil],
-
0x0b4d => [9, 0, nil, nil, nil, nil, nil],
-
0x0b5c => [0, 1, "\340\254\241\340\254\274", "\340\254\241\340\254\274", nil, nil, nil],
-
0x0b5d => [0, 1, "\340\254\242\340\254\274", "\340\254\242\340\254\274", nil, nil, nil],
-
0x0b94 => [0, 0, "\340\256\222\340\257\227", "\340\256\222\340\257\227", nil, nil, nil],
-
0x0bca => [0, 0, "\340\257\206\340\256\276", "\340\257\206\340\256\276", nil, nil, nil],
-
0x0bcb => [0, 0, "\340\257\207\340\256\276", "\340\257\207\340\256\276", nil, nil, nil],
-
0x0bcc => [0, 0, "\340\257\206\340\257\227", "\340\257\206\340\257\227", nil, nil, nil],
-
0x0bcd => [9, 0, nil, nil, nil, nil, nil],
-
0x0c48 => [0, 0, "\340\261\206\340\261\226", "\340\261\206\340\261\226", nil, nil, nil],
-
0x0c4d => [9, 0, nil, nil, nil, nil, nil],
-
0x0c55 => [84, 0, nil, nil, nil, nil, nil],
-
0x0c56 => [91, 0, nil, nil, nil, nil, nil],
-
0x0cc0 => [0, 0, "\340\262\277\340\263\225", "\340\262\277\340\263\225", nil, nil, nil],
-
0x0cc7 => [0, 0, "\340\263\206\340\263\225", "\340\263\206\340\263\225", nil, nil, nil],
-
0x0cc8 => [0, 0, "\340\263\206\340\263\226", "\340\263\206\340\263\226", nil, nil, nil],
-
0x0cca => [0, 0, "\340\263\206\340\263\202", "\340\263\206\340\263\202", nil, nil, nil],
-
0x0ccb => [0, 0, "\340\263\212\340\263\225", "\340\263\212\340\263\225", nil, nil, nil],
-
0x0ccd => [9, 0, nil, nil, nil, nil, nil],
-
0x0d4a => [0, 0, "\340\265\206\340\264\276", "\340\265\206\340\264\276", nil, nil, nil],
-
0x0d4b => [0, 0, "\340\265\207\340\264\276", "\340\265\207\340\264\276", nil, nil, nil],
-
0x0d4c => [0, 0, "\340\265\206\340\265\227", "\340\265\206\340\265\227", nil, nil, nil],
-
0x0d4d => [9, 0, nil, nil, nil, nil, nil],
-
0x0dca => [9, 0, nil, nil, nil, nil, nil],
-
0x0dda => [0, 0, "\340\267\231\340\267\212", "\340\267\231\340\267\212", nil, nil, nil],
-
0x0ddc => [0, 0, "\340\267\231\340\267\217", "\340\267\231\340\267\217", nil, nil, nil],
-
0x0ddd => [0, 0, "\340\267\234\340\267\212", "\340\267\234\340\267\212", nil, nil, nil],
-
0x0dde => [0, 0, "\340\267\231\340\267\237", "\340\267\231\340\267\237", nil, nil, nil],
-
0x0e33 => [0, 0, nil, "\340\271\215\340\270\262", nil, nil, nil],
-
0x0e38 => [103, 0, nil, nil, nil, nil, nil],
-
0x0e39 => [103, 0, nil, nil, nil, nil, nil],
-
0x0e3a => [9, 0, nil, nil, nil, nil, nil],
-
0x0e48 => [107, 0, nil, nil, nil, nil, nil],
-
0x0e49 => [107, 0, nil, nil, nil, nil, nil],
-
0x0e4a => [107, 0, nil, nil, nil, nil, nil],
-
0x0e4b => [107, 0, nil, nil, nil, nil, nil],
-
0x0eb3 => [0, 0, nil, "\340\273\215\340\272\262", nil, nil, nil],
-
0x0eb8 => [118, 0, nil, nil, nil, nil, nil],
-
0x0eb9 => [118, 0, nil, nil, nil, nil, nil],
-
0x0ec8 => [122, 0, nil, nil, nil, nil, nil],
-
0x0ec9 => [122, 0, nil, nil, nil, nil, nil],
-
0x0eca => [122, 0, nil, nil, nil, nil, nil],
-
0x0ecb => [122, 0, nil, nil, nil, nil, nil],
-
0x0edc => [0, 0, nil, "\340\272\253\340\272\231", nil, nil, nil],
-
0x0edd => [0, 0, nil, "\340\272\253\340\272\241", nil, nil, nil],
-
0x0f0c => [0, 0, nil, "\340\274\213", nil, nil, nil],
-
0x0f18 => [220, 0, nil, nil, nil, nil, nil],
-
0x0f19 => [220, 0, nil, nil, nil, nil, nil],
-
0x0f35 => [220, 0, nil, nil, nil, nil, nil],
-
0x0f37 => [220, 0, nil, nil, nil, nil, nil],
-
0x0f39 => [216, 0, nil, nil, nil, nil, nil],
-
0x0f43 => [0, 1, "\340\275\202\340\276\267", "\340\275\202\340\276\267", nil, nil, nil],
-
0x0f4d => [0, 1, "\340\275\214\340\276\267", "\340\275\214\340\276\267", nil, nil, nil],
-
0x0f52 => [0, 1, "\340\275\221\340\276\267", "\340\275\221\340\276\267", nil, nil, nil],
-
0x0f57 => [0, 1, "\340\275\226\340\276\267", "\340\275\226\340\276\267", nil, nil, nil],
-
0x0f5c => [0, 1, "\340\275\233\340\276\267", "\340\275\233\340\276\267", nil, nil, nil],
-
0x0f69 => [0, 1, "\340\275\200\340\276\265", "\340\275\200\340\276\265", nil, nil, nil],
-
0x0f71 => [129, 0, nil, nil, nil, nil, nil],
-
0x0f72 => [130, 0, nil, nil, nil, nil, nil],
-
0x0f73 => [0, 3, "\340\275\261\340\275\262", "\340\275\261\340\275\262", nil, nil, nil],
-
0x0f74 => [132, 0, nil, nil, nil, nil, nil],
-
0x0f75 => [0, 3, "\340\275\261\340\275\264", "\340\275\261\340\275\264", nil, nil, nil],
-
0x0f76 => [0, 1, "\340\276\262\340\276\200", "\340\276\262\340\276\200", nil, nil, nil],
-
0x0f77 => [0, 0, nil, "\340\276\262\340\276\201", nil, nil, nil],
-
0x0f78 => [0, 1, "\340\276\263\340\276\200", "\340\276\263\340\276\200", nil, nil, nil],
-
0x0f79 => [0, 0, nil, "\340\276\263\340\276\201", nil, nil, nil],
-
0x0f7a => [130, 0, nil, nil, nil, nil, nil],
-
0x0f7b => [130, 0, nil, nil, nil, nil, nil],
-
0x0f7c => [130, 0, nil, nil, nil, nil, nil],
-
0x0f7d => [130, 0, nil, nil, nil, nil, nil],
-
0x0f80 => [130, 0, nil, nil, nil, nil, nil],
-
0x0f81 => [0, 3, "\340\275\261\340\276\200", "\340\275\261\340\276\200", nil, nil, nil],
-
0x0f82 => [230, 0, nil, nil, nil, nil, nil],
-
0x0f83 => [230, 0, nil, nil, nil, nil, nil],
-
0x0f84 => [9, 0, nil, nil, nil, nil, nil],
-
0x0f86 => [230, 0, nil, nil, nil, nil, nil],
-
0x0f87 => [230, 0, nil, nil, nil, nil, nil],
-
0x0f93 => [0, 1, "\340\276\222\340\276\267", "\340\276\222\340\276\267", nil, nil, nil],
-
0x0f9d => [0, 1, "\340\276\234\340\276\267", "\340\276\234\340\276\267", nil, nil, nil],
-
0x0fa2 => [0, 1, "\340\276\241\340\276\267", "\340\276\241\340\276\267", nil, nil, nil],
-
0x0fa7 => [0, 1, "\340\276\246\340\276\267", "\340\276\246\340\276\267", nil, nil, nil],
-
0x0fac => [0, 1, "\340\276\253\340\276\267", "\340\276\253\340\276\267", nil, nil, nil],
-
0x0fb9 => [0, 1, "\340\276\220\340\276\265", "\340\276\220\340\276\265", nil, nil, nil],
-
0x0fc6 => [220, 0, nil, nil, nil, nil, nil],
-
0x1026 => [0, 0, "\341\200\245\341\200\256", "\341\200\245\341\200\256", nil, nil, nil],
-
0x1037 => [7, 0, nil, nil, nil, nil, nil],
-
0x1039 => [9, 0, nil, nil, nil, nil, nil],
-
0x17d2 => [9, 0, nil, nil, nil, nil, nil],
-
0x18a9 => [228, 0, nil, nil, nil, nil, nil],
-
0x1e00 => [0, 0, "A\314\245", "A\314\245", nil, 0x1e01, nil],
-
0x1e01 => [0, 0, "a\314\245", "a\314\245", 0x1e00, nil, 0x1e00],
-
0x1e02 => [0, 0, "B\314\207", "B\314\207", nil, 0x1e03, nil],
-
0x1e03 => [0, 0, "b\314\207", "b\314\207", 0x1e02, nil, 0x1e02],
-
0x1e04 => [0, 0, "B\314\243", "B\314\243", nil, 0x1e05, nil],
-
0x1e05 => [0, 0, "b\314\243", "b\314\243", 0x1e04, nil, 0x1e04],
-
0x1e06 => [0, 0, "B\314\261", "B\314\261", nil, 0x1e07, nil],
-
0x1e07 => [0, 0, "b\314\261", "b\314\261", 0x1e06, nil, 0x1e06],
-
0x1e08 => [0, 0, "\303\207\314\201", "\303\207\314\201", nil, 0x1e09, nil],
-
0x1e09 => [0, 0, "\303\247\314\201", "\303\247\314\201", 0x1e08, nil, 0x1e08],
-
0x1e0a => [0, 0, "D\314\207", "D\314\207", nil, 0x1e0b, nil],
-
0x1e0b => [0, 0, "d\314\207", "d\314\207", 0x1e0a, nil, 0x1e0a],
-
0x1e0c => [0, 0, "D\314\243", "D\314\243", nil, 0x1e0d, nil],
-
0x1e0d => [0, 0, "d\314\243", "d\314\243", 0x1e0c, nil, 0x1e0c],
-
0x1e0e => [0, 0, "D\314\261", "D\314\261", nil, 0x1e0f, nil],
-
0x1e0f => [0, 0, "d\314\261", "d\314\261", 0x1e0e, nil, 0x1e0e],
-
0x1e10 => [0, 0, "D\314\247", "D\314\247", nil, 0x1e11, nil],
-
0x1e11 => [0, 0, "d\314\247", "d\314\247", 0x1e10, nil, 0x1e10],
-
0x1e12 => [0, 0, "D\314\255", "D\314\255", nil, 0x1e13, nil],
-
0x1e13 => [0, 0, "d\314\255", "d\314\255", 0x1e12, nil, 0x1e12],
-
0x1e14 => [0, 0, "\304\222\314\200", "\304\222\314\200", nil, 0x1e15, nil],
-
0x1e15 => [0, 0, "\304\223\314\200", "\304\223\314\200", 0x1e14, nil, 0x1e14],
-
0x1e16 => [0, 0, "\304\222\314\201", "\304\222\314\201", nil, 0x1e17, nil],
-
0x1e17 => [0, 0, "\304\223\314\201", "\304\223\314\201", 0x1e16, nil, 0x1e16],
-
0x1e18 => [0, 0, "E\314\255", "E\314\255", nil, 0x1e19, nil],
-
0x1e19 => [0, 0, "e\314\255", "e\314\255", 0x1e18, nil, 0x1e18],
-
0x1e1a => [0, 0, "E\314\260", "E\314\260", nil, 0x1e1b, nil],
-
0x1e1b => [0, 0, "e\314\260", "e\314\260", 0x1e1a, nil, 0x1e1a],
-
0x1e1c => [0, 0, "\310\250\314\206", "\310\250\314\206", nil, 0x1e1d, nil],
-
0x1e1d => [0, 0, "\310\251\314\206", "\310\251\314\206", 0x1e1c, nil, 0x1e1c],
-
0x1e1e => [0, 0, "F\314\207", "F\314\207", nil, 0x1e1f, nil],
-
0x1e1f => [0, 0, "f\314\207", "f\314\207", 0x1e1e, nil, 0x1e1e],
-
0x1e20 => [0, 0, "G\314\204", "G\314\204", nil, 0x1e21, nil],
-
0x1e21 => [0, 0, "g\314\204", "g\314\204", 0x1e20, nil, 0x1e20],
-
0x1e22 => [0, 0, "H\314\207", "H\314\207", nil, 0x1e23, nil],
-
0x1e23 => [0, 0, "h\314\207", "h\314\207", 0x1e22, nil, 0x1e22],
-
0x1e24 => [0, 0, "H\314\243", "H\314\243", nil, 0x1e25, nil],
-
0x1e25 => [0, 0, "h\314\243", "h\314\243", 0x1e24, nil, 0x1e24],
-
0x1e26 => [0, 0, "H\314\210", "H\314\210", nil, 0x1e27, nil],
-
0x1e27 => [0, 0, "h\314\210", "h\314\210", 0x1e26, nil, 0x1e26],
-
0x1e28 => [0, 0, "H\314\247", "H\314\247", nil, 0x1e29, nil],
-
0x1e29 => [0, 0, "h\314\247", "h\314\247", 0x1e28, nil, 0x1e28],
-
0x1e2a => [0, 0, "H\314\256", "H\314\256", nil, 0x1e2b, nil],
-
0x1e2b => [0, 0, "h\314\256", "h\314\256", 0x1e2a, nil, 0x1e2a],
-
0x1e2c => [0, 0, "I\314\260", "I\314\260", nil, 0x1e2d, nil],
-
0x1e2d => [0, 0, "i\314\260", "i\314\260", 0x1e2c, nil, 0x1e2c],
-
0x1e2e => [0, 0, "\303\217\314\201", "\303\217\314\201", nil, 0x1e2f, nil],
-
0x1e2f => [0, 0, "\303\257\314\201", "\303\257\314\201", 0x1e2e, nil, 0x1e2e],
-
0x1e30 => [0, 0, "K\314\201", "K\314\201", nil, 0x1e31, nil],
-
0x1e31 => [0, 0, "k\314\201", "k\314\201", 0x1e30, nil, 0x1e30],
-
0x1e32 => [0, 0, "K\314\243", "K\314\243", nil, 0x1e33, nil],
-
0x1e33 => [0, 0, "k\314\243", "k\314\243", 0x1e32, nil, 0x1e32],
-
0x1e34 => [0, 0, "K\314\261", "K\314\261", nil, 0x1e35, nil],
-
0x1e35 => [0, 0, "k\314\261", "k\314\261", 0x1e34, nil, 0x1e34],
-
0x1e36 => [0, 0, "L\314\243", "L\314\243", nil, 0x1e37, nil],
-
0x1e37 => [0, 0, "l\314\243", "l\314\243", 0x1e36, nil, 0x1e36],
-
0x1e38 => [0, 0, "\341\270\266\314\204", "\341\270\266\314\204", nil, 0x1e39, nil],
-
0x1e39 => [0, 0, "\341\270\267\314\204", "\341\270\267\314\204", 0x1e38, nil, 0x1e38],
-
0x1e3a => [0, 0, "L\314\261", "L\314\261", nil, 0x1e3b, nil],
-
0x1e3b => [0, 0, "l\314\261", "l\314\261", 0x1e3a, nil, 0x1e3a],
-
0x1e3c => [0, 0, "L\314\255", "L\314\255", nil, 0x1e3d, nil],
-
0x1e3d => [0, 0, "l\314\255", "l\314\255", 0x1e3c, nil, 0x1e3c],
-
0x1e3e => [0, 0, "M\314\201", "M\314\201", nil, 0x1e3f, nil],
-
0x1e3f => [0, 0, "m\314\201", "m\314\201", 0x1e3e, nil, 0x1e3e],
-
0x1e40 => [0, 0, "M\314\207", "M\314\207", nil, 0x1e41, nil],
-
0x1e41 => [0, 0, "m\314\207", "m\314\207", 0x1e40, nil, 0x1e40],
-
0x1e42 => [0, 0, "M\314\243", "M\314\243", nil, 0x1e43, nil],
-
0x1e43 => [0, 0, "m\314\243", "m\314\243", 0x1e42, nil, 0x1e42],
-
0x1e44 => [0, 0, "N\314\207", "N\314\207", nil, 0x1e45, nil],
-
0x1e45 => [0, 0, "n\314\207", "n\314\207", 0x1e44, nil, 0x1e44],
-
0x1e46 => [0, 0, "N\314\243", "N\314\243", nil, 0x1e47, nil],
-
0x1e47 => [0, 0, "n\314\243", "n\314\243", 0x1e46, nil, 0x1e46],
-
0x1e48 => [0, 0, "N\314\261", "N\314\261", nil, 0x1e49, nil],
-
0x1e49 => [0, 0, "n\314\261", "n\314\261", 0x1e48, nil, 0x1e48],
-
0x1e4a => [0, 0, "N\314\255", "N\314\255", nil, 0x1e4b, nil],
-
0x1e4b => [0, 0, "n\314\255", "n\314\255", 0x1e4a, nil, 0x1e4a],
-
0x1e4c => [0, 0, "\303\225\314\201", "\303\225\314\201", nil, 0x1e4d, nil],
-
0x1e4d => [0, 0, "\303\265\314\201", "\303\265\314\201", 0x1e4c, nil, 0x1e4c],
-
0x1e4e => [0, 0, "\303\225\314\210", "\303\225\314\210", nil, 0x1e4f, nil],
-
0x1e4f => [0, 0, "\303\265\314\210", "\303\265\314\210", 0x1e4e, nil, 0x1e4e],
-
0x1e50 => [0, 0, "\305\214\314\200", "\305\214\314\200", nil, 0x1e51, nil],
-
0x1e51 => [0, 0, "\305\215\314\200", "\305\215\314\200", 0x1e50, nil, 0x1e50],
-
0x1e52 => [0, 0, "\305\214\314\201", "\305\214\314\201", nil, 0x1e53, nil],
-
0x1e53 => [0, 0, "\305\215\314\201", "\305\215\314\201", 0x1e52, nil, 0x1e52],
-
0x1e54 => [0, 0, "P\314\201", "P\314\201", nil, 0x1e55, nil],
-
0x1e55 => [0, 0, "p\314\201", "p\314\201", 0x1e54, nil, 0x1e54],
-
0x1e56 => [0, 0, "P\314\207", "P\314\207", nil, 0x1e57, nil],
-
0x1e57 => [0, 0, "p\314\207", "p\314\207", 0x1e56, nil, 0x1e56],
-
0x1e58 => [0, 0, "R\314\207", "R\314\207", nil, 0x1e59, nil],
-
0x1e59 => [0, 0, "r\314\207", "r\314\207", 0x1e58, nil, 0x1e58],
-
0x1e5a => [0, 0, "R\314\243", "R\314\243", nil, 0x1e5b, nil],
-
0x1e5b => [0, 0, "r\314\243", "r\314\243", 0x1e5a, nil, 0x1e5a],
-
0x1e5c => [0, 0, "\341\271\232\314\204", "\341\271\232\314\204", nil, 0x1e5d, nil],
-
0x1e5d => [0, 0, "\341\271\233\314\204", "\341\271\233\314\204", 0x1e5c, nil, 0x1e5c],
-
0x1e5e => [0, 0, "R\314\261", "R\314\261", nil, 0x1e5f, nil],
-
0x1e5f => [0, 0, "r\314\261", "r\314\261", 0x1e5e, nil, 0x1e5e],
-
0x1e60 => [0, 0, "S\314\207", "S\314\207", nil, 0x1e61, nil],
-
0x1e61 => [0, 0, "s\314\207", "s\314\207", 0x1e60, nil, 0x1e60],
-
0x1e62 => [0, 0, "S\314\243", "S\314\243", nil, 0x1e63, nil],
-
0x1e63 => [0, 0, "s\314\243", "s\314\243", 0x1e62, nil, 0x1e62],
-
0x1e64 => [0, 0, "\305\232\314\207", "\305\232\314\207", nil, 0x1e65, nil],
-
0x1e65 => [0, 0, "\305\233\314\207", "\305\233\314\207", 0x1e64, nil, 0x1e64],
-
0x1e66 => [0, 0, "\305\240\314\207", "\305\240\314\207", nil, 0x1e67, nil],
-
0x1e67 => [0, 0, "\305\241\314\207", "\305\241\314\207", 0x1e66, nil, 0x1e66],
-
0x1e68 => [0, 0, "\341\271\242\314\207", "\341\271\242\314\207", nil, 0x1e69, nil],
-
0x1e69 => [0, 0, "\341\271\243\314\207", "\341\271\243\314\207", 0x1e68, nil, 0x1e68],
-
0x1e6a => [0, 0, "T\314\207", "T\314\207", nil, 0x1e6b, nil],
-
0x1e6b => [0, 0, "t\314\207", "t\314\207", 0x1e6a, nil, 0x1e6a],
-
0x1e6c => [0, 0, "T\314\243", "T\314\243", nil, 0x1e6d, nil],
-
0x1e6d => [0, 0, "t\314\243", "t\314\243", 0x1e6c, nil, 0x1e6c],
-
0x1e6e => [0, 0, "T\314\261", "T\314\261", nil, 0x1e6f, nil],
-
0x1e6f => [0, 0, "t\314\261", "t\314\261", 0x1e6e, nil, 0x1e6e],
-
0x1e70 => [0, 0, "T\314\255", "T\314\255", nil, 0x1e71, nil],
-
0x1e71 => [0, 0, "t\314\255", "t\314\255", 0x1e70, nil, 0x1e70],
-
0x1e72 => [0, 0, "U\314\244", "U\314\244", nil, 0x1e73, nil],
-
0x1e73 => [0, 0, "u\314\244", "u\314\244", 0x1e72, nil, 0x1e72],
-
0x1e74 => [0, 0, "U\314\260", "U\314\260", nil, 0x1e75, nil],
-
0x1e75 => [0, 0, "u\314\260", "u\314\260", 0x1e74, nil, 0x1e74],
-
0x1e76 => [0, 0, "U\314\255", "U\314\255", nil, 0x1e77, nil],
-
0x1e77 => [0, 0, "u\314\255", "u\314\255", 0x1e76, nil, 0x1e76],
-
0x1e78 => [0, 0, "\305\250\314\201", "\305\250\314\201", nil, 0x1e79, nil],
-
0x1e79 => [0, 0, "\305\251\314\201", "\305\251\314\201", 0x1e78, nil, 0x1e78],
-
0x1e7a => [0, 0, "\305\252\314\210", "\305\252\314\210", nil, 0x1e7b, nil],
-
0x1e7b => [0, 0, "\305\253\314\210", "\305\253\314\210", 0x1e7a, nil, 0x1e7a],
-
0x1e7c => [0, 0, "V\314\203", "V\314\203", nil, 0x1e7d, nil],
-
0x1e7d => [0, 0, "v\314\203", "v\314\203", 0x1e7c, nil, 0x1e7c],
-
0x1e7e => [0, 0, "V\314\243", "V\314\243", nil, 0x1e7f, nil],
-
0x1e7f => [0, 0, "v\314\243", "v\314\243", 0x1e7e, nil, 0x1e7e],
-
0x1e80 => [0, 0, "W\314\200", "W\314\200", nil, 0x1e81, nil],
-
0x1e81 => [0, 0, "w\314\200", "w\314\200", 0x1e80, nil, 0x1e80],
-
0x1e82 => [0, 0, "W\314\201", "W\314\201", nil, 0x1e83, nil],
-
0x1e83 => [0, 0, "w\314\201", "w\314\201", 0x1e82, nil, 0x1e82],
-
0x1e84 => [0, 0, "W\314\210", "W\314\210", nil, 0x1e85, nil],
-
0x1e85 => [0, 0, "w\314\210", "w\314\210", 0x1e84, nil, 0x1e84],
-
0x1e86 => [0, 0, "W\314\207", "W\314\207", nil, 0x1e87, nil],
-
0x1e87 => [0, 0, "w\314\207", "w\314\207", 0x1e86, nil, 0x1e86],
-
0x1e88 => [0, 0, "W\314\243", "W\314\243", nil, 0x1e89, nil],
-
0x1e89 => [0, 0, "w\314\243", "w\314\243", 0x1e88, nil, 0x1e88],
-
0x1e8a => [0, 0, "X\314\207", "X\314\207", nil, 0x1e8b, nil],
-
0x1e8b => [0, 0, "x\314\207", "x\314\207", 0x1e8a, nil, 0x1e8a],
-
0x1e8c => [0, 0, "X\314\210", "X\314\210", nil, 0x1e8d, nil],
-
0x1e8d => [0, 0, "x\314\210", "x\314\210", 0x1e8c, nil, 0x1e8c],
-
0x1e8e => [0, 0, "Y\314\207", "Y\314\207", nil, 0x1e8f, nil],
-
0x1e8f => [0, 0, "y\314\207", "y\314\207", 0x1e8e, nil, 0x1e8e],
-
0x1e90 => [0, 0, "Z\314\202", "Z\314\202", nil, 0x1e91, nil],
-
0x1e91 => [0, 0, "z\314\202", "z\314\202", 0x1e90, nil, 0x1e90],
-
0x1e92 => [0, 0, "Z\314\243", "Z\314\243", nil, 0x1e93, nil],
-
0x1e93 => [0, 0, "z\314\243", "z\314\243", 0x1e92, nil, 0x1e92],
-
0x1e94 => [0, 0, "Z\314\261", "Z\314\261", nil, 0x1e95, nil],
-
0x1e95 => [0, 0, "z\314\261", "z\314\261", 0x1e94, nil, 0x1e94],
-
0x1e96 => [0, 0, "h\314\261", "h\314\261", nil, nil, nil],
-
0x1e97 => [0, 0, "t\314\210", "t\314\210", nil, nil, nil],
-
0x1e98 => [0, 0, "w\314\212", "w\314\212", nil, nil, nil],
-
0x1e99 => [0, 0, "y\314\212", "y\314\212", nil, nil, nil],
-
0x1e9a => [0, 0, nil, "a\312\276", nil, nil, nil],
-
0x1e9b => [0, 0, "\305\277\314\207", "\305\277\314\207", 0x1e60, nil, 0x1e60],
-
0x1ea0 => [0, 0, "A\314\243", "A\314\243", nil, 0x1ea1, nil],
-
0x1ea1 => [0, 0, "a\314\243", "a\314\243", 0x1ea0, nil, 0x1ea0],
-
0x1ea2 => [0, 0, "A\314\211", "A\314\211", nil, 0x1ea3, nil],
-
0x1ea3 => [0, 0, "a\314\211", "a\314\211", 0x1ea2, nil, 0x1ea2],
-
0x1ea4 => [0, 0, "\303\202\314\201", "\303\202\314\201", nil, 0x1ea5, nil],
-
0x1ea5 => [0, 0, "\303\242\314\201", "\303\242\314\201", 0x1ea4, nil, 0x1ea4],
-
0x1ea6 => [0, 0, "\303\202\314\200", "\303\202\314\200", nil, 0x1ea7, nil],
-
0x1ea7 => [0, 0, "\303\242\314\200", "\303\242\314\200", 0x1ea6, nil, 0x1ea6],
-
0x1ea8 => [0, 0, "\303\202\314\211", "\303\202\314\211", nil, 0x1ea9, nil],
-
0x1ea9 => [0, 0, "\303\242\314\211", "\303\242\314\211", 0x1ea8, nil, 0x1ea8],
-
0x1eaa => [0, 0, "\303\202\314\203", "\303\202\314\203", nil, 0x1eab, nil],
-
0x1eab => [0, 0, "\303\242\314\203", "\303\242\314\203", 0x1eaa, nil, 0x1eaa],
-
0x1eac => [0, 0, "\341\272\240\314\202", "\341\272\240\314\202", nil, 0x1ead, nil],
-
0x1ead => [0, 0, "\341\272\241\314\202", "\341\272\241\314\202", 0x1eac, nil, 0x1eac],
-
0x1eae => [0, 0, "\304\202\314\201", "\304\202\314\201", nil, 0x1eaf, nil],
-
0x1eaf => [0, 0, "\304\203\314\201", "\304\203\314\201", 0x1eae, nil, 0x1eae],
-
0x1eb0 => [0, 0, "\304\202\314\200", "\304\202\314\200", nil, 0x1eb1, nil],
-
0x1eb1 => [0, 0, "\304\203\314\200", "\304\203\314\200", 0x1eb0, nil, 0x1eb0],
-
0x1eb2 => [0, 0, "\304\202\314\211", "\304\202\314\211", nil, 0x1eb3, nil],
-
0x1eb3 => [0, 0, "\304\203\314\211", "\304\203\314\211", 0x1eb2, nil, 0x1eb2],
-
0x1eb4 => [0, 0, "\304\202\314\203", "\304\202\314\203", nil, 0x1eb5, nil],
-
0x1eb5 => [0, 0, "\304\203\314\203", "\304\203\314\203", 0x1eb4, nil, 0x1eb4],
-
0x1eb6 => [0, 0, "\341\272\240\314\206", "\341\272\240\314\206", nil, 0x1eb7, nil],
-
0x1eb7 => [0, 0, "\341\272\241\314\206", "\341\272\241\314\206", 0x1eb6, nil, 0x1eb6],
-
0x1eb8 => [0, 0, "E\314\243", "E\314\243", nil, 0x1eb9, nil],
-
0x1eb9 => [0, 0, "e\314\243", "e\314\243", 0x1eb8, nil, 0x1eb8],
-
0x1eba => [0, 0, "E\314\211", "E\314\211", nil, 0x1ebb, nil],
-
0x1ebb => [0, 0, "e\314\211", "e\314\211", 0x1eba, nil, 0x1eba],
-
0x1ebc => [0, 0, "E\314\203", "E\314\203", nil, 0x1ebd, nil],
-
0x1ebd => [0, 0, "e\314\203", "e\314\203", 0x1ebc, nil, 0x1ebc],
-
0x1ebe => [0, 0, "\303\212\314\201", "\303\212\314\201", nil, 0x1ebf, nil],
-
0x1ebf => [0, 0, "\303\252\314\201", "\303\252\314\201", 0x1ebe, nil, 0x1ebe],
-
0x1ec0 => [0, 0, "\303\212\314\200", "\303\212\314\200", nil, 0x1ec1, nil],
-
0x1ec1 => [0, 0, "\303\252\314\200", "\303\252\314\200", 0x1ec0, nil, 0x1ec0],
-
0x1ec2 => [0, 0, "\303\212\314\211", "\303\212\314\211", nil, 0x1ec3, nil],
-
0x1ec3 => [0, 0, "\303\252\314\211", "\303\252\314\211", 0x1ec2, nil, 0x1ec2],
-
0x1ec4 => [0, 0, "\303\212\314\203", "\303\212\314\203", nil, 0x1ec5, nil],
-
0x1ec5 => [0, 0, "\303\252\314\203", "\303\252\314\203", 0x1ec4, nil, 0x1ec4],
-
0x1ec6 => [0, 0, "\341\272\270\314\202", "\341\272\270\314\202", nil, 0x1ec7, nil],
-
0x1ec7 => [0, 0, "\341\272\271\314\202", "\341\272\271\314\202", 0x1ec6, nil, 0x1ec6],
-
0x1ec8 => [0, 0, "I\314\211", "I\314\211", nil, 0x1ec9, nil],
-
0x1ec9 => [0, 0, "i\314\211", "i\314\211", 0x1ec8, nil, 0x1ec8],
-
0x1eca => [0, 0, "I\314\243", "I\314\243", nil, 0x1ecb, nil],
-
0x1ecb => [0, 0, "i\314\243", "i\314\243", 0x1eca, nil, 0x1eca],
-
0x1ecc => [0, 0, "O\314\243", "O\314\243", nil, 0x1ecd, nil],
-
0x1ecd => [0, 0, "o\314\243", "o\314\243", 0x1ecc, nil, 0x1ecc],
-
0x1ece => [0, 0, "O\314\211", "O\314\211", nil, 0x1ecf, nil],
-
0x1ecf => [0, 0, "o\314\211", "o\314\211", 0x1ece, nil, 0x1ece],
-
0x1ed0 => [0, 0, "\303\224\314\201", "\303\224\314\201", nil, 0x1ed1, nil],
-
0x1ed1 => [0, 0, "\303\264\314\201", "\303\264\314\201", 0x1ed0, nil, 0x1ed0],
-
0x1ed2 => [0, 0, "\303\224\314\200", "\303\224\314\200", nil, 0x1ed3, nil],
-
0x1ed3 => [0, 0, "\303\264\314\200", "\303\264\314\200", 0x1ed2, nil, 0x1ed2],
-
0x1ed4 => [0, 0, "\303\224\314\211", "\303\224\314\211", nil, 0x1ed5, nil],
-
0x1ed5 => [0, 0, "\303\264\314\211", "\303\264\314\211", 0x1ed4, nil, 0x1ed4],
-
0x1ed6 => [0, 0, "\303\224\314\203", "\303\224\314\203", nil, 0x1ed7, nil],
-
0x1ed7 => [0, 0, "\303\264\314\203", "\303\264\314\203", 0x1ed6, nil, 0x1ed6],
-
0x1ed8 => [0, 0, "\341\273\214\314\202", "\341\273\214\314\202", nil, 0x1ed9, nil],
-
0x1ed9 => [0, 0, "\341\273\215\314\202", "\341\273\215\314\202", 0x1ed8, nil, 0x1ed8],
-
0x1eda => [0, 0, "\306\240\314\201", "\306\240\314\201", nil, 0x1edb, nil],
-
0x1edb => [0, 0, "\306\241\314\201", "\306\241\314\201", 0x1eda, nil, 0x1eda],
-
0x1edc => [0, 0, "\306\240\314\200", "\306\240\314\200", nil, 0x1edd, nil],
-
0x1edd => [0, 0, "\306\241\314\200", "\306\241\314\200", 0x1edc, nil, 0x1edc],
-
0x1ede => [0, 0, "\306\240\314\211", "\306\240\314\211", nil, 0x1edf, nil],
-
0x1edf => [0, 0, "\306\241\314\211", "\306\241\314\211", 0x1ede, nil, 0x1ede],
-
0x1ee0 => [0, 0, "\306\240\314\203", "\306\240\314\203", nil, 0x1ee1, nil],
-
0x1ee1 => [0, 0, "\306\241\314\203", "\306\241\314\203", 0x1ee0, nil, 0x1ee0],
-
0x1ee2 => [0, 0, "\306\240\314\243", "\306\240\314\243", nil, 0x1ee3, nil],
-
0x1ee3 => [0, 0, "\306\241\314\243", "\306\241\314\243", 0x1ee2, nil, 0x1ee2],
-
0x1ee4 => [0, 0, "U\314\243", "U\314\243", nil, 0x1ee5, nil],
-
0x1ee5 => [0, 0, "u\314\243", "u\314\243", 0x1ee4, nil, 0x1ee4],
-
0x1ee6 => [0, 0, "U\314\211", "U\314\211", nil, 0x1ee7, nil],
-
0x1ee7 => [0, 0, "u\314\211", "u\314\211", 0x1ee6, nil, 0x1ee6],
-
0x1ee8 => [0, 0, "\306\257\314\201", "\306\257\314\201", nil, 0x1ee9, nil],
-
0x1ee9 => [0, 0, "\306\260\314\201", "\306\260\314\201", 0x1ee8, nil, 0x1ee8],
-
0x1eea => [0, 0, "\306\257\314\200", "\306\257\314\200", nil, 0x1eeb, nil],
-
0x1eeb => [0, 0, "\306\260\314\200", "\306\260\314\200", 0x1eea, nil, 0x1eea],
-
0x1eec => [0, 0, "\306\257\314\211", "\306\257\314\211", nil, 0x1eed, nil],
-
0x1eed => [0, 0, "\306\260\314\211", "\306\260\314\211", 0x1eec, nil, 0x1eec],
-
0x1eee => [0, 0, "\306\257\314\203", "\306\257\314\203", nil, 0x1eef, nil],
-
0x1eef => [0, 0, "\306\260\314\203", "\306\260\314\203", 0x1eee, nil, 0x1eee],
-
0x1ef0 => [0, 0, "\306\257\314\243", "\306\257\314\243", nil, 0x1ef1, nil],
-
0x1ef1 => [0, 0, "\306\260\314\243", "\306\260\314\243", 0x1ef0, nil, 0x1ef0],
-
0x1ef2 => [0, 0, "Y\314\200", "Y\314\200", nil, 0x1ef3, nil],
-
0x1ef3 => [0, 0, "y\314\200", "y\314\200", 0x1ef2, nil, 0x1ef2],
-
0x1ef4 => [0, 0, "Y\314\243", "Y\314\243", nil, 0x1ef5, nil],
-
0x1ef5 => [0, 0, "y\314\243", "y\314\243", 0x1ef4, nil, 0x1ef4],
-
0x1ef6 => [0, 0, "Y\314\211", "Y\314\211", nil, 0x1ef7, nil],
-
0x1ef7 => [0, 0, "y\314\211", "y\314\211", 0x1ef6, nil, 0x1ef6],
-
0x1ef8 => [0, 0, "Y\314\203", "Y\314\203", nil, 0x1ef9, nil],
-
0x1ef9 => [0, 0, "y\314\203", "y\314\203", 0x1ef8, nil, 0x1ef8],
-
0x1f00 => [0, 0, "\316\261\314\223", "\316\261\314\223", 0x1f08, nil, 0x1f08],
-
0x1f01 => [0, 0, "\316\261\314\224", "\316\261\314\224", 0x1f09, nil, 0x1f09],
-
0x1f02 => [0, 0, "\341\274\200\314\200", "\341\274\200\314\200", 0x1f0a, nil, 0x1f0a],
-
0x1f03 => [0, 0, "\341\274\201\314\200", "\341\274\201\314\200", 0x1f0b, nil, 0x1f0b],
-
0x1f04 => [0, 0, "\341\274\200\314\201", "\341\274\200\314\201", 0x1f0c, nil, 0x1f0c],
-
0x1f05 => [0, 0, "\341\274\201\314\201", "\341\274\201\314\201", 0x1f0d, nil, 0x1f0d],
-
0x1f06 => [0, 0, "\341\274\200\315\202", "\341\274\200\315\202", 0x1f0e, nil, 0x1f0e],
-
0x1f07 => [0, 0, "\341\274\201\315\202", "\341\274\201\315\202", 0x1f0f, nil, 0x1f0f],
-
0x1f08 => [0, 0, "\316\221\314\223", "\316\221\314\223", nil, 0x1f00, nil],
-
0x1f09 => [0, 0, "\316\221\314\224", "\316\221\314\224", nil, 0x1f01, nil],
-
0x1f0a => [0, 0, "\341\274\210\314\200", "\341\274\210\314\200", nil, 0x1f02, nil],
-
0x1f0b => [0, 0, "\341\274\211\314\200", "\341\274\211\314\200", nil, 0x1f03, nil],
-
0x1f0c => [0, 0, "\341\274\210\314\201", "\341\274\210\314\201", nil, 0x1f04, nil],
-
0x1f0d => [0, 0, "\341\274\211\314\201", "\341\274\211\314\201", nil, 0x1f05, nil],
-
0x1f0e => [0, 0, "\341\274\210\315\202", "\341\274\210\315\202", nil, 0x1f06, nil],
-
0x1f0f => [0, 0, "\341\274\211\315\202", "\341\274\211\315\202", nil, 0x1f07, nil],
-
0x1f10 => [0, 0, "\316\265\314\223", "\316\265\314\223", 0x1f18, nil, 0x1f18],
-
0x1f11 => [0, 0, "\316\265\314\224", "\316\265\314\224", 0x1f19, nil, 0x1f19],
-
0x1f12 => [0, 0, "\341\274\220\314\200", "\341\274\220\314\200", 0x1f1a, nil, 0x1f1a],
-
0x1f13 => [0, 0, "\341\274\221\314\200", "\341\274\221\314\200", 0x1f1b, nil, 0x1f1b],
-
0x1f14 => [0, 0, "\341\274\220\314\201", "\341\274\220\314\201", 0x1f1c, nil, 0x1f1c],
-
0x1f15 => [0, 0, "\341\274\221\314\201", "\341\274\221\314\201", 0x1f1d, nil, 0x1f1d],
-
0x1f18 => [0, 0, "\316\225\314\223", "\316\225\314\223", nil, 0x1f10, nil],
-
0x1f19 => [0, 0, "\316\225\314\224", "\316\225\314\224", nil, 0x1f11, nil],
-
0x1f1a => [0, 0, "\341\274\230\314\200", "\341\274\230\314\200", nil, 0x1f12, nil],
-
0x1f1b => [0, 0, "\341\274\231\314\200", "\341\274\231\314\200", nil, 0x1f13, nil],
-
0x1f1c => [0, 0, "\341\274\230\314\201", "\341\274\230\314\201", nil, 0x1f14, nil],
-
0x1f1d => [0, 0, "\341\274\231\314\201", "\341\274\231\314\201", nil, 0x1f15, nil],
-
0x1f20 => [0, 0, "\316\267\314\223", "\316\267\314\223", 0x1f28, nil, 0x1f28],
-
0x1f21 => [0, 0, "\316\267\314\224", "\316\267\314\224", 0x1f29, nil, 0x1f29],
-
0x1f22 => [0, 0, "\341\274\240\314\200", "\341\274\240\314\200", 0x1f2a, nil, 0x1f2a],
-
0x1f23 => [0, 0, "\341\274\241\314\200", "\341\274\241\314\200", 0x1f2b, nil, 0x1f2b],
-
0x1f24 => [0, 0, "\341\274\240\314\201", "\341\274\240\314\201", 0x1f2c, nil, 0x1f2c],
-
0x1f25 => [0, 0, "\341\274\241\314\201", "\341\274\241\314\201", 0x1f2d, nil, 0x1f2d],
-
0x1f26 => [0, 0, "\341\274\240\315\202", "\341\274\240\315\202", 0x1f2e, nil, 0x1f2e],
-
0x1f27 => [0, 0, "\341\274\241\315\202", "\341\274\241\315\202", 0x1f2f, nil, 0x1f2f],
-
0x1f28 => [0, 0, "\316\227\314\223", "\316\227\314\223", nil, 0x1f20, nil],
-
0x1f29 => [0, 0, "\316\227\314\224", "\316\227\314\224", nil, 0x1f21, nil],
-
0x1f2a => [0, 0, "\341\274\250\314\200", "\341\274\250\314\200", nil, 0x1f22, nil],
-
0x1f2b => [0, 0, "\341\274\251\314\200", "\341\274\251\314\200", nil, 0x1f23, nil],
-
0x1f2c => [0, 0, "\341\274\250\314\201", "\341\274\250\314\201", nil, 0x1f24, nil],
-
0x1f2d => [0, 0, "\341\274\251\314\201", "\341\274\251\314\201", nil, 0x1f25, nil],
-
0x1f2e => [0, 0, "\341\274\250\315\202", "\341\274\250\315\202", nil, 0x1f26, nil],
-
0x1f2f => [0, 0, "\341\274\251\315\202", "\341\274\251\315\202", nil, 0x1f27, nil],
-
0x1f30 => [0, 0, "\316\271\314\223", "\316\271\314\223", 0x1f38, nil, 0x1f38],
-
0x1f31 => [0, 0, "\316\271\314\224", "\316\271\314\224", 0x1f39, nil, 0x1f39],
-
0x1f32 => [0, 0, "\341\274\260\314\200", "\341\274\260\314\200", 0x1f3a, nil, 0x1f3a],
-
0x1f33 => [0, 0, "\341\274\261\314\200", "\341\274\261\314\200", 0x1f3b, nil, 0x1f3b],
-
0x1f34 => [0, 0, "\341\274\260\314\201", "\341\274\260\314\201", 0x1f3c, nil, 0x1f3c],
-
0x1f35 => [0, 0, "\341\274\261\314\201", "\341\274\261\314\201", 0x1f3d, nil, 0x1f3d],
-
0x1f36 => [0, 0, "\341\274\260\315\202", "\341\274\260\315\202", 0x1f3e, nil, 0x1f3e],
-
0x1f37 => [0, 0, "\341\274\261\315\202", "\341\274\261\315\202", 0x1f3f, nil, 0x1f3f],
-
0x1f38 => [0, 0, "\316\231\314\223", "\316\231\314\223", nil, 0x1f30, nil],
-
0x1f39 => [0, 0, "\316\231\314\224", "\316\231\314\224", nil, 0x1f31, nil],
-
0x1f3a => [0, 0, "\341\274\270\314\200", "\341\274\270\314\200", nil, 0x1f32, nil],
-
0x1f3b => [0, 0, "\341\274\271\314\200", "\341\274\271\314\200", nil, 0x1f33, nil],
-
0x1f3c => [0, 0, "\341\274\270\314\201", "\341\274\270\314\201", nil, 0x1f34, nil],
-
0x1f3d => [0, 0, "\341\274\271\314\201", "\341\274\271\314\201", nil, 0x1f35, nil],
-
0x1f3e => [0, 0, "\341\274\270\315\202", "\341\274\270\315\202", nil, 0x1f36, nil],
-
0x1f3f => [0, 0, "\341\274\271\315\202", "\341\274\271\315\202", nil, 0x1f37, nil],
-
0x1f40 => [0, 0, "\316\277\314\223", "\316\277\314\223", 0x1f48, nil, 0x1f48],
-
0x1f41 => [0, 0, "\316\277\314\224", "\316\277\314\224", 0x1f49, nil, 0x1f49],
-
0x1f42 => [0, 0, "\341\275\200\314\200", "\341\275\200\314\200", 0x1f4a, nil, 0x1f4a],
-
0x1f43 => [0, 0, "\341\275\201\314\200", "\341\275\201\314\200", 0x1f4b, nil, 0x1f4b],
-
0x1f44 => [0, 0, "\341\275\200\314\201", "\341\275\200\314\201", 0x1f4c, nil, 0x1f4c],
-
0x1f45 => [0, 0, "\341\275\201\314\201", "\341\275\201\314\201", 0x1f4d, nil, 0x1f4d],
-
0x1f48 => [0, 0, "\316\237\314\223", "\316\237\314\223", nil, 0x1f40, nil],
-
0x1f49 => [0, 0, "\316\237\314\224", "\316\237\314\224", nil, 0x1f41, nil],
-
0x1f4a => [0, 0, "\341\275\210\314\200", "\341\275\210\314\200", nil, 0x1f42, nil],
-
0x1f4b => [0, 0, "\341\275\211\314\200", "\341\275\211\314\200", nil, 0x1f43, nil],
-
0x1f4c => [0, 0, "\341\275\210\314\201", "\341\275\210\314\201", nil, 0x1f44, nil],
-
0x1f4d => [0, 0, "\341\275\211\314\201", "\341\275\211\314\201", nil, 0x1f45, nil],
-
0x1f50 => [0, 0, "\317\205\314\223", "\317\205\314\223", nil, nil, nil],
-
0x1f51 => [0, 0, "\317\205\314\224", "\317\205\314\224", 0x1f59, nil, 0x1f59],
-
0x1f52 => [0, 0, "\341\275\220\314\200", "\341\275\220\314\200", nil, nil, nil],
-
0x1f53 => [0, 0, "\341\275\221\314\200", "\341\275\221\314\200", 0x1f5b, nil, 0x1f5b],
-
0x1f54 => [0, 0, "\341\275\220\314\201", "\341\275\220\314\201", nil, nil, nil],
-
0x1f55 => [0, 0, "\341\275\221\314\201", "\341\275\221\314\201", 0x1f5d, nil, 0x1f5d],
-
0x1f56 => [0, 0, "\341\275\220\315\202", "\341\275\220\315\202", nil, nil, nil],
-
0x1f57 => [0, 0, "\341\275\221\315\202", "\341\275\221\315\202", 0x1f5f, nil, 0x1f5f],
-
0x1f59 => [0, 0, "\316\245\314\224", "\316\245\314\224", nil, 0x1f51, nil],
-
0x1f5b => [0, 0, "\341\275\231\314\200", "\341\275\231\314\200", nil, 0x1f53, nil],
-
0x1f5d => [0, 0, "\341\275\231\314\201", "\341\275\231\314\201", nil, 0x1f55, nil],
-
0x1f5f => [0, 0, "\341\275\231\315\202", "\341\275\231\315\202", nil, 0x1f57, nil],
-
0x1f60 => [0, 0, "\317\211\314\223", "\317\211\314\223", 0x1f68, nil, 0x1f68],
-
0x1f61 => [0, 0, "\317\211\314\224", "\317\211\314\224", 0x1f69, nil, 0x1f69],
-
0x1f62 => [0, 0, "\341\275\240\314\200", "\341\275\240\314\200", 0x1f6a, nil, 0x1f6a],
-
0x1f63 => [0, 0, "\341\275\241\314\200", "\341\275\241\314\200", 0x1f6b, nil, 0x1f6b],
-
0x1f64 => [0, 0, "\341\275\240\314\201", "\341\275\240\314\201", 0x1f6c, nil, 0x1f6c],
-
0x1f65 => [0, 0, "\341\275\241\314\201", "\341\275\241\314\201", 0x1f6d, nil, 0x1f6d],
-
0x1f66 => [0, 0, "\341\275\240\315\202", "\341\275\240\315\202", 0x1f6e, nil, 0x1f6e],
-
0x1f67 => [0, 0, "\341\275\241\315\202", "\341\275\241\315\202", 0x1f6f, nil, 0x1f6f],
-
0x1f68 => [0, 0, "\316\251\314\223", "\316\251\314\223", nil, 0x1f60, nil],
-
0x1f69 => [0, 0, "\316\251\314\224", "\316\251\314\224", nil, 0x1f61, nil],
-
0x1f6a => [0, 0, "\341\275\250\314\200", "\341\275\250\314\200", nil, 0x1f62, nil],
-
0x1f6b => [0, 0, "\341\275\251\314\200", "\341\275\251\314\200", nil, 0x1f63, nil],
-
0x1f6c => [0, 0, "\341\275\250\314\201", "\341\275\250\314\201", nil, 0x1f64, nil],
-
0x1f6d => [0, 0, "\341\275\251\314\201", "\341\275\251\314\201", nil, 0x1f65, nil],
-
0x1f6e => [0, 0, "\341\275\250\315\202", "\341\275\250\315\202", nil, 0x1f66, nil],
-
0x1f6f => [0, 0, "\341\275\251\315\202", "\341\275\251\315\202", nil, 0x1f67, nil],
-
0x1f70 => [0, 0, "\316\261\314\200", "\316\261\314\200", 0x1fba, nil, 0x1fba],
-
0x1f71 => [0, 2, "\316\254", "\316\254", 0x1fbb, nil, 0x1fbb],
-
0x1f72 => [0, 0, "\316\265\314\200", "\316\265\314\200", 0x1fc8, nil, 0x1fc8],
-
0x1f73 => [0, 2, "\316\255", "\316\255", 0x1fc9, nil, 0x1fc9],
-
0x1f74 => [0, 0, "\316\267\314\200", "\316\267\314\200", 0x1fca, nil, 0x1fca],
-
0x1f75 => [0, 2, "\316\256", "\316\256", 0x1fcb, nil, 0x1fcb],
-
0x1f76 => [0, 0, "\316\271\314\200", "\316\271\314\200", 0x1fda, nil, 0x1fda],
-
0x1f77 => [0, 2, "\316\257", "\316\257", 0x1fdb, nil, 0x1fdb],
-
0x1f78 => [0, 0, "\316\277\314\200", "\316\277\314\200", 0x1ff8, nil, 0x1ff8],
-
0x1f79 => [0, 2, "\317\214", "\317\214", 0x1ff9, nil, 0x1ff9],
-
0x1f7a => [0, 0, "\317\205\314\200", "\317\205\314\200", 0x1fea, nil, 0x1fea],
-
0x1f7b => [0, 2, "\317\215", "\317\215", 0x1feb, nil, 0x1feb],
-
0x1f7c => [0, 0, "\317\211\314\200", "\317\211\314\200", 0x1ffa, nil, 0x1ffa],
-
0x1f7d => [0, 2, "\317\216", "\317\216", 0x1ffb, nil, 0x1ffb],
-
0x1f80 => [0, 0, "\341\274\200\315\205", "\341\274\200\315\205", 0x1f88, nil, 0x1f88],
-
0x1f81 => [0, 0, "\341\274\201\315\205", "\341\274\201\315\205", 0x1f89, nil, 0x1f89],
-
0x1f82 => [0, 0, "\341\274\202\315\205", "\341\274\202\315\205", 0x1f8a, nil, 0x1f8a],
-
0x1f83 => [0, 0, "\341\274\203\315\205", "\341\274\203\315\205", 0x1f8b, nil, 0x1f8b],
-
0x1f84 => [0, 0, "\341\274\204\315\205", "\341\274\204\315\205", 0x1f8c, nil, 0x1f8c],
-
0x1f85 => [0, 0, "\341\274\205\315\205", "\341\274\205\315\205", 0x1f8d, nil, 0x1f8d],
-
0x1f86 => [0, 0, "\341\274\206\315\205", "\341\274\206\315\205", 0x1f8e, nil, 0x1f8e],
-
0x1f87 => [0, 0, "\341\274\207\315\205", "\341\274\207\315\205", 0x1f8f, nil, 0x1f8f],
-
0x1f88 => [0, 0, "\341\274\210\315\205", "\341\274\210\315\205", nil, 0x1f80, nil],
-
0x1f89 => [0, 0, "\341\274\211\315\205", "\341\274\211\315\205", nil, 0x1f81, nil],
-
0x1f8a => [0, 0, "\341\274\212\315\205", "\341\274\212\315\205", nil, 0x1f82, nil],
-
0x1f8b => [0, 0, "\341\274\213\315\205", "\341\274\213\315\205", nil, 0x1f83, nil],
-
0x1f8c => [0, 0, "\341\274\214\315\205", "\341\274\214\315\205", nil, 0x1f84, nil],
-
0x1f8d => [0, 0, "\341\274\215\315\205", "\341\274\215\315\205", nil, 0x1f85, nil],
-
0x1f8e => [0, 0, "\341\274\216\315\205", "\341\274\216\315\205", nil, 0x1f86, nil],
-
0x1f8f => [0, 0, "\341\274\217\315\205", "\341\274\217\315\205", nil, 0x1f87, nil],
-
0x1f90 => [0, 0, "\341\274\240\315\205", "\341\274\240\315\205", 0x1f98, nil, 0x1f98],
-
0x1f91 => [0, 0, "\341\274\241\315\205", "\341\274\241\315\205", 0x1f99, nil, 0x1f99],
-
0x1f92 => [0, 0, "\341\274\242\315\205", "\341\274\242\315\205", 0x1f9a, nil, 0x1f9a],
-
0x1f93 => [0, 0, "\341\274\243\315\205", "\341\274\243\315\205", 0x1f9b, nil, 0x1f9b],
-
0x1f94 => [0, 0, "\341\274\244\315\205", "\341\274\244\315\205", 0x1f9c, nil, 0x1f9c],
-
0x1f95 => [0, 0, "\341\274\245\315\205", "\341\274\245\315\205", 0x1f9d, nil, 0x1f9d],
-
0x1f96 => [0, 0, "\341\274\246\315\205", "\341\274\246\315\205", 0x1f9e, nil, 0x1f9e],
-
0x1f97 => [0, 0, "\341\274\247\315\205", "\341\274\247\315\205", 0x1f9f, nil, 0x1f9f],
-
0x1f98 => [0, 0, "\341\274\250\315\205", "\341\274\250\315\205", nil, 0x1f90, nil],
-
0x1f99 => [0, 0, "\341\274\251\315\205", "\341\274\251\315\205", nil, 0x1f91, nil],
-
0x1f9a => [0, 0, "\341\274\252\315\205", "\341\274\252\315\205", nil, 0x1f92, nil],
-
0x1f9b => [0, 0, "\341\274\253\315\205", "\341\274\253\315\205", nil, 0x1f93, nil],
-
0x1f9c => [0, 0, "\341\274\254\315\205", "\341\274\254\315\205", nil, 0x1f94, nil],
-
0x1f9d => [0, 0, "\341\274\255\315\205", "\341\274\255\315\205", nil, 0x1f95, nil],
-
0x1f9e => [0, 0, "\341\274\256\315\205", "\341\274\256\315\205", nil, 0x1f96, nil],
-
0x1f9f => [0, 0, "\341\274\257\315\205", "\341\274\257\315\205", nil, 0x1f97, nil],
-
0x1fa0 => [0, 0, "\341\275\240\315\205", "\341\275\240\315\205", 0x1fa8, nil, 0x1fa8],
-
0x1fa1 => [0, 0, "\341\275\241\315\205", "\341\275\241\315\205", 0x1fa9, nil, 0x1fa9],
-
0x1fa2 => [0, 0, "\341\275\242\315\205", "\341\275\242\315\205", 0x1faa, nil, 0x1faa],
-
0x1fa3 => [0, 0, "\341\275\243\315\205", "\341\275\243\315\205", 0x1fab, nil, 0x1fab],
-
0x1fa4 => [0, 0, "\341\275\244\315\205", "\341\275\244\315\205", 0x1fac, nil, 0x1fac],
-
0x1fa5 => [0, 0, "\341\275\245\315\205", "\341\275\245\315\205", 0x1fad, nil, 0x1fad],
-
0x1fa6 => [0, 0, "\341\275\246\315\205", "\341\275\246\315\205", 0x1fae, nil, 0x1fae],
-
0x1fa7 => [0, 0, "\341\275\247\315\205", "\341\275\247\315\205", 0x1faf, nil, 0x1faf],
-
0x1fa8 => [0, 0, "\341\275\250\315\205", "\341\275\250\315\205", nil, 0x1fa0, nil],
-
0x1fa9 => [0, 0, "\341\275\251\315\205", "\341\275\251\315\205", nil, 0x1fa1, nil],
-
0x1faa => [0, 0, "\341\275\252\315\205", "\341\275\252\315\205", nil, 0x1fa2, nil],
-
0x1fab => [0, 0, "\341\275\253\315\205", "\341\275\253\315\205", nil, 0x1fa3, nil],
-
0x1fac => [0, 0, "\341\275\254\315\205", "\341\275\254\315\205", nil, 0x1fa4, nil],
-
0x1fad => [0, 0, "\341\275\255\315\205", "\341\275\255\315\205", nil, 0x1fa5, nil],
-
0x1fae => [0, 0, "\341\275\256\315\205", "\341\275\256\315\205", nil, 0x1fa6, nil],
-
0x1faf => [0, 0, "\341\275\257\315\205", "\341\275\257\315\205", nil, 0x1fa7, nil],
-
0x1fb0 => [0, 0, "\316\261\314\206", "\316\261\314\206", 0x1fb8, nil, 0x1fb8],
-
0x1fb1 => [0, 0, "\316\261\314\204", "\316\261\314\204", 0x1fb9, nil, 0x1fb9],
-
0x1fb2 => [0, 0, "\341\275\260\315\205", "\341\275\260\315\205", nil, nil, nil],
-
0x1fb3 => [0, 0, "\316\261\315\205", "\316\261\315\205", 0x1fbc, nil, 0x1fbc],
-
0x1fb4 => [0, 0, "\316\254\315\205", "\316\254\315\205", nil, nil, nil],
-
0x1fb6 => [0, 0, "\316\261\315\202", "\316\261\315\202", nil, nil, nil],
-
0x1fb7 => [0, 0, "\341\276\266\315\205", "\341\276\266\315\205", nil, nil, nil],
-
0x1fb8 => [0, 0, "\316\221\314\206", "\316\221\314\206", nil, 0x1fb0, nil],
-
0x1fb9 => [0, 0, "\316\221\314\204", "\316\221\314\204", nil, 0x1fb1, nil],
-
0x1fba => [0, 0, "\316\221\314\200", "\316\221\314\200", nil, 0x1f70, nil],
-
0x1fbb => [0, 2, "\316\206", "\316\206", nil, 0x1f71, nil],
-
0x1fbc => [0, 0, "\316\221\315\205", "\316\221\315\205", nil, 0x1fb3, nil],
-
0x1fbd => [0, 0, nil, " \314\223", nil, nil, nil],
-
0x1fbe => [0, 2, "\316\271", "\316\271", 0x0399, nil, 0x0399],
-
0x1fbf => [0, 0, nil, " \314\223", nil, nil, nil],
-
0x1fc0 => [0, 0, nil, " \315\202", nil, nil, nil],
-
0x1fc1 => [0, 0, "\302\250\315\202", "\302\250\315\202", nil, nil, nil],
-
0x1fc2 => [0, 0, "\341\275\264\315\205", "\341\275\264\315\205", nil, nil, nil],
-
0x1fc3 => [0, 0, "\316\267\315\205", "\316\267\315\205", 0x1fcc, nil, 0x1fcc],
-
0x1fc4 => [0, 0, "\316\256\315\205", "\316\256\315\205", nil, nil, nil],
-
0x1fc6 => [0, 0, "\316\267\315\202", "\316\267\315\202", nil, nil, nil],
-
0x1fc7 => [0, 0, "\341\277\206\315\205", "\341\277\206\315\205", nil, nil, nil],
-
0x1fc8 => [0, 0, "\316\225\314\200", "\316\225\314\200", nil, 0x1f72, nil],
-
0x1fc9 => [0, 2, "\316\210", "\316\210", nil, 0x1f73, nil],
-
0x1fca => [0, 0, "\316\227\314\200", "\316\227\314\200", nil, 0x1f74, nil],
-
0x1fcb => [0, 2, "\316\211", "\316\211", nil, 0x1f75, nil],
-
0x1fcc => [0, 0, "\316\227\315\205", "\316\227\315\205", nil, 0x1fc3, nil],
-
0x1fcd => [0, 0, "\341\276\277\314\200", "\341\276\277\314\200", nil, nil, nil],
-
0x1fce => [0, 0, "\341\276\277\314\201", "\341\276\277\314\201", nil, nil, nil],
-
0x1fcf => [0, 0, "\341\276\277\315\202", "\341\276\277\315\202", nil, nil, nil],
-
0x1fd0 => [0, 0, "\316\271\314\206", "\316\271\314\206", 0x1fd8, nil, 0x1fd8],
-
0x1fd1 => [0, 0, "\316\271\314\204", "\316\271\314\204", 0x1fd9, nil, 0x1fd9],
-
0x1fd2 => [0, 0, "\317\212\314\200", "\317\212\314\200", nil, nil, nil],
-
0x1fd3 => [0, 2, "\316\220", "\316\220", nil, nil, nil],
-
0x1fd6 => [0, 0, "\316\271\315\202", "\316\271\315\202", nil, nil, nil],
-
0x1fd7 => [0, 0, "\317\212\315\202", "\317\212\315\202", nil, nil, nil],
-
0x1fd8 => [0, 0, "\316\231\314\206", "\316\231\314\206", nil, 0x1fd0, nil],
-
0x1fd9 => [0, 0, "\316\231\314\204", "\316\231\314\204", nil, 0x1fd1, nil],
-
0x1fda => [0, 0, "\316\231\314\200", "\316\231\314\200", nil, 0x1f76, nil],
-
0x1fdb => [0, 2, "\316\212", "\316\212", nil, 0x1f77, nil],
-
0x1fdd => [0, 0, "\341\277\276\314\200", "\341\277\276\314\200", nil, nil, nil],
-
0x1fde => [0, 0, "\341\277\276\314\201", "\341\277\276\314\201", nil, nil, nil],
-
0x1fdf => [0, 0, "\341\277\276\315\202", "\341\277\276\315\202", nil, nil, nil],
-
0x1fe0 => [0, 0, "\317\205\314\206", "\317\205\314\206", 0x1fe8, nil, 0x1fe8],
-
0x1fe1 => [0, 0, "\317\205\314\204", "\317\205\314\204", 0x1fe9, nil, 0x1fe9],
-
0x1fe2 => [0, 0, "\317\213\314\200", "\317\213\314\200", nil, nil, nil],
-
0x1fe3 => [0, 2, "\316\260", "\316\260", nil, nil, nil],
-
0x1fe4 => [0, 0, "\317\201\314\223", "\317\201\314\223", nil, nil, nil],
-
0x1fe5 => [0, 0, "\317\201\314\224", "\317\201\314\224", 0x1fec, nil, 0x1fec],
-
0x1fe6 => [0, 0, "\317\205\315\202", "\317\205\315\202", nil, nil, nil],
-
0x1fe7 => [0, 0, "\317\213\315\202", "\317\213\315\202", nil, nil, nil],
-
0x1fe8 => [0, 0, "\316\245\314\206", "\316\245\314\206", nil, 0x1fe0, nil],
-
0x1fe9 => [0, 0, "\316\245\314\204", "\316\245\314\204", nil, 0x1fe1, nil],
-
0x1fea => [0, 0, "\316\245\314\200", "\316\245\314\200", nil, 0x1f7a, nil],
-
0x1feb => [0, 2, "\316\216", "\316\216", nil, 0x1f7b, nil],
-
0x1fec => [0, 0, "\316\241\314\224", "\316\241\314\224", nil, 0x1fe5, nil],
-
0x1fed => [0, 0, "\302\250\314\200", "\302\250\314\200", nil, nil, nil],
-
0x1fee => [0, 2, "\316\205", "\316\205", nil, nil, nil],
-
0x1fef => [0, 2, "`", "`", nil, nil, nil],
-
0x1ff2 => [0, 0, "\341\275\274\315\205", "\341\275\274\315\205", nil, nil, nil],
-
0x1ff3 => [0, 0, "\317\211\315\205", "\317\211\315\205", 0x1ffc, nil, 0x1ffc],
-
0x1ff4 => [0, 0, "\317\216\315\205", "\317\216\315\205", nil, nil, nil],
-
0x1ff6 => [0, 0, "\317\211\315\202", "\317\211\315\202", nil, nil, nil],
-
0x1ff7 => [0, 0, "\341\277\266\315\205", "\341\277\266\315\205", nil, nil, nil],
-
0x1ff8 => [0, 0, "\316\237\314\200", "\316\237\314\200", nil, 0x1f78, nil],
-
0x1ff9 => [0, 2, "\316\214", "\316\214", nil, 0x1f79, nil],
-
0x1ffa => [0, 0, "\316\251\314\200", "\316\251\314\200", nil, 0x1f7c, nil],
-
0x1ffb => [0, 2, "\316\217", "\316\217", nil, 0x1f7d, nil],
-
0x1ffc => [0, 0, "\316\251\315\205", "\316\251\315\205", nil, 0x1ff3, nil],
-
0x1ffd => [0, 2, "\302\264", "\302\264", nil, nil, nil],
-
0x1ffe => [0, 0, nil, " \314\224", nil, nil, nil],
-
0x2000 => [0, 2, "\342\200\202", "\342\200\202", nil, nil, nil],
-
0x2001 => [0, 2, "\342\200\203", "\342\200\203", nil, nil, nil],
-
0x2002 => [0, 0, nil, " ", nil, nil, nil],
-
0x2003 => [0, 0, nil, " ", nil, nil, nil],
-
0x2004 => [0, 0, nil, " ", nil, nil, nil],
-
0x2005 => [0, 0, nil, " ", nil, nil, nil],
-
0x2006 => [0, 0, nil, " ", nil, nil, nil],
-
0x2007 => [0, 0, nil, " ", nil, nil, nil],
-
0x2008 => [0, 0, nil, " ", nil, nil, nil],
-
0x2009 => [0, 0, nil, " ", nil, nil, nil],
-
0x200a => [0, 0, nil, " ", nil, nil, nil],
-
0x2011 => [0, 0, nil, "\342\200\220", nil, nil, nil],
-
0x2017 => [0, 0, nil, " \314\263", nil, nil, nil],
-
0x2024 => [0, 0, nil, ".", nil, nil, nil],
-
0x2025 => [0, 0, nil, "..", nil, nil, nil],
-
0x2026 => [0, 0, nil, "...", nil, nil, nil],
-
0x202f => [0, 0, nil, " ", nil, nil, nil],
-
0x2033 => [0, 0, nil, "\342\200\262\342\200\262", nil, nil, nil],
-
0x2034 => [0, 0, nil, "\342\200\262\342\200\262\342\200\262", nil, nil, nil],
-
0x2036 => [0, 0, nil, "\342\200\265\342\200\265", nil, nil, nil],
-
0x2037 => [0, 0, nil, "\342\200\265\342\200\265\342\200\265", nil, nil, nil],
-
0x203c => [0, 0, nil, "!!", nil, nil, nil],
-
0x203e => [0, 0, nil, " \314\205", nil, nil, nil],
-
0x2048 => [0, 0, nil, "?!", nil, nil, nil],
-
0x2049 => [0, 0, nil, "!?", nil, nil, nil],
-
0x2070 => [0, 0, nil, "0", nil, nil, nil],
-
0x2074 => [0, 0, nil, "4", nil, nil, nil],
-
0x2075 => [0, 0, nil, "5", nil, nil, nil],
-
0x2076 => [0, 0, nil, "6", nil, nil, nil],
-
0x2077 => [0, 0, nil, "7", nil, nil, nil],
-
0x2078 => [0, 0, nil, "8", nil, nil, nil],
-
0x2079 => [0, 0, nil, "9", nil, nil, nil],
-
0x207a => [0, 0, nil, "+", nil, nil, nil],
-
0x207b => [0, 0, nil, "\342\210\222", nil, nil, nil],
-
0x207c => [0, 0, nil, "=", nil, nil, nil],
-
0x207d => [0, 0, nil, "(", nil, nil, nil],
-
0x207e => [0, 0, nil, ")", nil, nil, nil],
-
0x207f => [0, 0, nil, "n", nil, nil, nil],
-
0x2080 => [0, 0, nil, "0", nil, nil, nil],
-
0x2081 => [0, 0, nil, "1", nil, nil, nil],
-
0x2082 => [0, 0, nil, "2", nil, nil, nil],
-
0x2083 => [0, 0, nil, "3", nil, nil, nil],
-
0x2084 => [0, 0, nil, "4", nil, nil, nil],
-
0x2085 => [0, 0, nil, "5", nil, nil, nil],
-
0x2086 => [0, 0, nil, "6", nil, nil, nil],
-
0x2087 => [0, 0, nil, "7", nil, nil, nil],
-
0x2088 => [0, 0, nil, "8", nil, nil, nil],
-
0x2089 => [0, 0, nil, "9", nil, nil, nil],
-
0x208a => [0, 0, nil, "+", nil, nil, nil],
-
0x208b => [0, 0, nil, "\342\210\222", nil, nil, nil],
-
0x208c => [0, 0, nil, "=", nil, nil, nil],
-
0x208d => [0, 0, nil, "(", nil, nil, nil],
-
0x208e => [0, 0, nil, ")", nil, nil, nil],
-
0x20a8 => [0, 0, nil, "Rs", nil, nil, nil],
-
0x20d0 => [230, 0, nil, nil, nil, nil, nil],
-
0x20d1 => [230, 0, nil, nil, nil, nil, nil],
-
0x20d2 => [1, 0, nil, nil, nil, nil, nil],
-
0x20d3 => [1, 0, nil, nil, nil, nil, nil],
-
0x20d4 => [230, 0, nil, nil, nil, nil, nil],
-
0x20d5 => [230, 0, nil, nil, nil, nil, nil],
-
0x20d6 => [230, 0, nil, nil, nil, nil, nil],
-
0x20d7 => [230, 0, nil, nil, nil, nil, nil],
-
0x20d8 => [1, 0, nil, nil, nil, nil, nil],
-
0x20d9 => [1, 0, nil, nil, nil, nil, nil],
-
0x20da => [1, 0, nil, nil, nil, nil, nil],
-
0x20db => [230, 0, nil, nil, nil, nil, nil],
-
0x20dc => [230, 0, nil, nil, nil, nil, nil],
-
0x20e1 => [230, 0, nil, nil, nil, nil, nil],
-
0x2100 => [0, 0, nil, "a/c", nil, nil, nil],
-
0x2101 => [0, 0, nil, "a/s", nil, nil, nil],
-
0x2102 => [0, 0, nil, "C", nil, nil, nil],
-
0x2103 => [0, 0, nil, "\302\260C", nil, nil, nil],
-
0x2105 => [0, 0, nil, "c/o", nil, nil, nil],
-
0x2106 => [0, 0, nil, "c/u", nil, nil, nil],
-
0x2107 => [0, 0, nil, "\306\220", nil, nil, nil],
-
0x2109 => [0, 0, nil, "\302\260F", nil, nil, nil],
-
0x210a => [0, 0, nil, "g", nil, nil, nil],
-
0x210b => [0, 0, nil, "H", nil, nil, nil],
-
0x210c => [0, 0, nil, "H", nil, nil, nil],
-
0x210d => [0, 0, nil, "H", nil, nil, nil],
-
0x210e => [0, 0, nil, "h", nil, nil, nil],
-
0x210f => [0, 0, nil, "\304\247", nil, nil, nil],
-
0x2110 => [0, 0, nil, "I", nil, nil, nil],
-
0x2111 => [0, 0, nil, "I", nil, nil, nil],
-
0x2112 => [0, 0, nil, "L", nil, nil, nil],
-
0x2113 => [0, 0, nil, "l", nil, nil, nil],
-
0x2115 => [0, 0, nil, "N", nil, nil, nil],
-
0x2116 => [0, 0, nil, "No", nil, nil, nil],
-
0x2119 => [0, 0, nil, "P", nil, nil, nil],
-
0x211a => [0, 0, nil, "Q", nil, nil, nil],
-
0x211b => [0, 0, nil, "R", nil, nil, nil],
-
0x211c => [0, 0, nil, "R", nil, nil, nil],
-
0x211d => [0, 0, nil, "R", nil, nil, nil],
-
0x2120 => [0, 0, nil, "SM", nil, nil, nil],
-
0x2121 => [0, 0, nil, "TEL", nil, nil, nil],
-
0x2122 => [0, 0, nil, "TM", nil, nil, nil],
-
0x2124 => [0, 0, nil, "Z", nil, nil, nil],
-
0x2126 => [0, 2, "\316\251", "\316\251", nil, 0x03c9, nil],
-
0x2128 => [0, 0, nil, "Z", nil, nil, nil],
-
0x212a => [0, 2, "K", "K", nil, 0x006b, nil],
-
0x212b => [0, 2, "\303\205", "\303\205", nil, 0x00e5, nil],
-
0x212c => [0, 0, nil, "B", nil, nil, nil],
-
0x212d => [0, 0, nil, "C", nil, nil, nil],
-
0x212f => [0, 0, nil, "e", nil, nil, nil],
-
0x2130 => [0, 0, nil, "E", nil, nil, nil],
-
0x2131 => [0, 0, nil, "F", nil, nil, nil],
-
0x2133 => [0, 0, nil, "M", nil, nil, nil],
-
0x2134 => [0, 0, nil, "o", nil, nil, nil],
-
0x2135 => [0, 0, nil, "\327\220", nil, nil, nil],
-
0x2136 => [0, 0, nil, "\327\221", nil, nil, nil],
-
0x2137 => [0, 0, nil, "\327\222", nil, nil, nil],
-
0x2138 => [0, 0, nil, "\327\223", nil, nil, nil],
-
0x2139 => [0, 0, nil, "i", nil, nil, nil],
-
0x2153 => [0, 0, nil, "1\342\201\2043", nil, nil, nil],
-
0x2154 => [0, 0, nil, "2\342\201\2043", nil, nil, nil],
-
0x2155 => [0, 0, nil, "1\342\201\2045", nil, nil, nil],
-
0x2156 => [0, 0, nil, "2\342\201\2045", nil, nil, nil],
-
0x2157 => [0, 0, nil, "3\342\201\2045", nil, nil, nil],
-
0x2158 => [0, 0, nil, "4\342\201\2045", nil, nil, nil],
-
0x2159 => [0, 0, nil, "1\342\201\2046", nil, nil, nil],
-
0x215a => [0, 0, nil, "5\342\201\2046", nil, nil, nil],
-
0x215b => [0, 0, nil, "1\342\201\2048", nil, nil, nil],
-
0x215c => [0, 0, nil, "3\342\201\2048", nil, nil, nil],
-
0x215d => [0, 0, nil, "5\342\201\2048", nil, nil, nil],
-
0x215e => [0, 0, nil, "7\342\201\2048", nil, nil, nil],
-
0x215f => [0, 0, nil, "1\342\201\204", nil, nil, nil],
-
0x2160 => [0, 0, nil, "I", nil, 0x2170, nil],
-
0x2161 => [0, 0, nil, "II", nil, 0x2171, nil],
-
0x2162 => [0, 0, nil, "III", nil, 0x2172, nil],
-
0x2163 => [0, 0, nil, "IV", nil, 0x2173, nil],
-
0x2164 => [0, 0, nil, "V", nil, 0x2174, nil],
-
0x2165 => [0, 0, nil, "VI", nil, 0x2175, nil],
-
0x2166 => [0, 0, nil, "VII", nil, 0x2176, nil],
-
0x2167 => [0, 0, nil, "VIII", nil, 0x2177, nil],
-
0x2168 => [0, 0, nil, "IX", nil, 0x2178, nil],
-
0x2169 => [0, 0, nil, "X", nil, 0x2179, nil],
-
0x216a => [0, 0, nil, "XI", nil, 0x217a, nil],
-
0x216b => [0, 0, nil, "XII", nil, 0x217b, nil],
-
0x216c => [0, 0, nil, "L", nil, 0x217c, nil],
-
0x216d => [0, 0, nil, "C", nil, 0x217d, nil],
-
0x216e => [0, 0, nil, "D", nil, 0x217e, nil],
-
0x216f => [0, 0, nil, "M", nil, 0x217f, nil],
-
0x2170 => [0, 0, nil, "i", 0x2160, nil, 0x2160],
-
0x2171 => [0, 0, nil, "ii", 0x2161, nil, 0x2161],
-
0x2172 => [0, 0, nil, "iii", 0x2162, nil, 0x2162],
-
0x2173 => [0, 0, nil, "iv", 0x2163, nil, 0x2163],
-
0x2174 => [0, 0, nil, "v", 0x2164, nil, 0x2164],
-
0x2175 => [0, 0, nil, "vi", 0x2165, nil, 0x2165],
-
0x2176 => [0, 0, nil, "vii", 0x2166, nil, 0x2166],
-
0x2177 => [0, 0, nil, "viii", 0x2167, nil, 0x2167],
-
0x2178 => [0, 0, nil, "ix", 0x2168, nil, 0x2168],
-
0x2179 => [0, 0, nil, "x", 0x2169, nil, 0x2169],
-
0x217a => [0, 0, nil, "xi", 0x216a, nil, 0x216a],
-
0x217b => [0, 0, nil, "xii", 0x216b, nil, 0x216b],
-
0x217c => [0, 0, nil, "l", 0x216c, nil, 0x216c],
-
0x217d => [0, 0, nil, "c", 0x216d, nil, 0x216d],
-
0x217e => [0, 0, nil, "d", 0x216e, nil, 0x216e],
-
0x217f => [0, 0, nil, "m", 0x216f, nil, 0x216f],
-
0x219a => [0, 0, "\342\206\220\314\270", "\342\206\220\314\270", nil, nil, nil],
-
0x219b => [0, 0, "\342\206\222\314\270", "\342\206\222\314\270", nil, nil, nil],
-
0x21ae => [0, 0, "\342\206\224\314\270", "\342\206\224\314\270", nil, nil, nil],
-
0x21cd => [0, 0, "\342\207\220\314\270", "\342\207\220\314\270", nil, nil, nil],
-
0x21ce => [0, 0, "\342\207\224\314\270", "\342\207\224\314\270", nil, nil, nil],
-
0x21cf => [0, 0, "\342\207\222\314\270", "\342\207\222\314\270", nil, nil, nil],
-
0x2204 => [0, 0, "\342\210\203\314\270", "\342\210\203\314\270", nil, nil, nil],
-
0x2209 => [0, 0, "\342\210\210\314\270", "\342\210\210\314\270", nil, nil, nil],
-
0x220c => [0, 0, "\342\210\213\314\270", "\342\210\213\314\270", nil, nil, nil],
-
0x2224 => [0, 0, "\342\210\243\314\270", "\342\210\243\314\270", nil, nil, nil],
-
0x2226 => [0, 0, "\342\210\245\314\270", "\342\210\245\314\270", nil, nil, nil],
-
0x222c => [0, 0, nil, "\342\210\253\342\210\253", nil, nil, nil],
-
0x222d => [0, 0, nil, "\342\210\253\342\210\253\342\210\253", nil, nil, nil],
-
0x222f => [0, 0, nil, "\342\210\256\342\210\256", nil, nil, nil],
-
0x2230 => [0, 0, nil, "\342\210\256\342\210\256\342\210\256", nil, nil, nil],
-
0x2241 => [0, 0, "\342\210\274\314\270", "\342\210\274\314\270", nil, nil, nil],
-
0x2244 => [0, 0, "\342\211\203\314\270", "\342\211\203\314\270", nil, nil, nil],
-
0x2247 => [0, 0, "\342\211\205\314\270", "\342\211\205\314\270", nil, nil, nil],
-
0x2249 => [0, 0, "\342\211\210\314\270", "\342\211\210\314\270", nil, nil, nil],
-
0x2260 => [0, 0, "=\314\270", "=\314\270", nil, nil, nil],
-
0x2262 => [0, 0, "\342\211\241\314\270", "\342\211\241\314\270", nil, nil, nil],
-
0x226d => [0, 0, "\342\211\215\314\270", "\342\211\215\314\270", nil, nil, nil],
-
0x226e => [0, 0, "<\314\270", "<\314\270", nil, nil, nil],
-
0x226f => [0, 0, ">\314\270", ">\314\270", nil, nil, nil],
-
0x2270 => [0, 0, "\342\211\244\314\270", "\342\211\244\314\270", nil, nil, nil],
-
0x2271 => [0, 0, "\342\211\245\314\270", "\342\211\245\314\270", nil, nil, nil],
-
0x2274 => [0, 0, "\342\211\262\314\270", "\342\211\262\314\270", nil, nil, nil],
-
0x2275 => [0, 0, "\342\211\263\314\270", "\342\211\263\314\270", nil, nil, nil],
-
0x2278 => [0, 0, "\342\211\266\314\270", "\342\211\266\314\270", nil, nil, nil],
-
0x2279 => [0, 0, "\342\211\267\314\270", "\342\211\267\314\270", nil, nil, nil],
-
0x2280 => [0, 0, "\342\211\272\314\270", "\342\211\272\314\270", nil, nil, nil],
-
0x2281 => [0, 0, "\342\211\273\314\270", "\342\211\273\314\270", nil, nil, nil],
-
0x2284 => [0, 0, "\342\212\202\314\270", "\342\212\202\314\270", nil, nil, nil],
-
0x2285 => [0, 0, "\342\212\203\314\270", "\342\212\203\314\270", nil, nil, nil],
-
0x2288 => [0, 0, "\342\212\206\314\270", "\342\212\206\314\270", nil, nil, nil],
-
0x2289 => [0, 0, "\342\212\207\314\270", "\342\212\207\314\270", nil, nil, nil],
-
0x22ac => [0, 0, "\342\212\242\314\270", "\342\212\242\314\270", nil, nil, nil],
-
0x22ad => [0, 0, "\342\212\250\314\270", "\342\212\250\314\270", nil, nil, nil],
-
0x22ae => [0, 0, "\342\212\251\314\270", "\342\212\251\314\270", nil, nil, nil],
-
0x22af => [0, 0, "\342\212\253\314\270", "\342\212\253\314\270", nil, nil, nil],
-
0x22e0 => [0, 0, "\342\211\274\314\270", "\342\211\274\314\270", nil, nil, nil],
-
0x22e1 => [0, 0, "\342\211\275\314\270", "\342\211\275\314\270", nil, nil, nil],
-
0x22e2 => [0, 0, "\342\212\221\314\270", "\342\212\221\314\270", nil, nil, nil],
-
0x22e3 => [0, 0, "\342\212\222\314\270", "\342\212\222\314\270", nil, nil, nil],
-
0x22ea => [0, 0, "\342\212\262\314\270", "\342\212\262\314\270", nil, nil, nil],
-
0x22eb => [0, 0, "\342\212\263\314\270", "\342\212\263\314\270", nil, nil, nil],
-
0x22ec => [0, 0, "\342\212\264\314\270", "\342\212\264\314\270", nil, nil, nil],
-
0x22ed => [0, 0, "\342\212\265\314\270", "\342\212\265\314\270", nil, nil, nil],
-
0x2329 => [0, 2, "\343\200\210", "\343\200\210", nil, nil, nil],
-
0x232a => [0, 2, "\343\200\211", "\343\200\211", nil, nil, nil],
-
0x2460 => [0, 0, nil, "1", nil, nil, nil],
-
0x2461 => [0, 0, nil, "2", nil, nil, nil],
-
0x2462 => [0, 0, nil, "3", nil, nil, nil],
-
0x2463 => [0, 0, nil, "4", nil, nil, nil],
-
0x2464 => [0, 0, nil, "5", nil, nil, nil],
-
0x2465 => [0, 0, nil, "6", nil, nil, nil],
-
0x2466 => [0, 0, nil, "7", nil, nil, nil],
-
0x2467 => [0, 0, nil, "8", nil, nil, nil],
-
0x2468 => [0, 0, nil, "9", nil, nil, nil],
-
0x2469 => [0, 0, nil, "10", nil, nil, nil],
-
0x246a => [0, 0, nil, "11", nil, nil, nil],
-
0x246b => [0, 0, nil, "12", nil, nil, nil],
-
0x246c => [0, 0, nil, "13", nil, nil, nil],
-
0x246d => [0, 0, nil, "14", nil, nil, nil],
-
0x246e => [0, 0, nil, "15", nil, nil, nil],
-
0x246f => [0, 0, nil, "16", nil, nil, nil],
-
0x2470 => [0, 0, nil, "17", nil, nil, nil],
-
0x2471 => [0, 0, nil, "18", nil, nil, nil],
-
0x2472 => [0, 0, nil, "19", nil, nil, nil],
-
0x2473 => [0, 0, nil, "20", nil, nil, nil],
-
0x2474 => [0, 0, nil, "(1)", nil, nil, nil],
-
0x2475 => [0, 0, nil, "(2)", nil, nil, nil],
-
0x2476 => [0, 0, nil, "(3)", nil, nil, nil],
-
0x2477 => [0, 0, nil, "(4)", nil, nil, nil],
-
0x2478 => [0, 0, nil, "(5)", nil, nil, nil],
-
0x2479 => [0, 0, nil, "(6)", nil, nil, nil],
-
0x247a => [0, 0, nil, "(7)", nil, nil, nil],
-
0x247b => [0, 0, nil, "(8)", nil, nil, nil],
-
0x247c => [0, 0, nil, "(9)", nil, nil, nil],
-
0x247d => [0, 0, nil, "(10)", nil, nil, nil],
-
0x247e => [0, 0, nil, "(11)", nil, nil, nil],
-
0x247f => [0, 0, nil, "(12)", nil, nil, nil],
-
0x2480 => [0, 0, nil, "(13)", nil, nil, nil],
-
0x2481 => [0, 0, nil, "(14)", nil, nil, nil],
-
0x2482 => [0, 0, nil, "(15)", nil, nil, nil],
-
0x2483 => [0, 0, nil, "(16)", nil, nil, nil],
-
0x2484 => [0, 0, nil, "(17)", nil, nil, nil],
-
0x2485 => [0, 0, nil, "(18)", nil, nil, nil],
-
0x2486 => [0, 0, nil, "(19)", nil, nil, nil],
-
0x2487 => [0, 0, nil, "(20)", nil, nil, nil],
-
0x2488 => [0, 0, nil, "1.", nil, nil, nil],
-
0x2489 => [0, 0, nil, "2.", nil, nil, nil],
-
0x248a => [0, 0, nil, "3.", nil, nil, nil],
-
0x248b => [0, 0, nil, "4.", nil, nil, nil],
-
0x248c => [0, 0, nil, "5.", nil, nil, nil],
-
0x248d => [0, 0, nil, "6.", nil, nil, nil],
-
0x248e => [0, 0, nil, "7.", nil, nil, nil],
-
0x248f => [0, 0, nil, "8.", nil, nil, nil],
-
0x2490 => [0, 0, nil, "9.", nil, nil, nil],
-
0x2491 => [0, 0, nil, "10.", nil, nil, nil],
-
0x2492 => [0, 0, nil, "11.", nil, nil, nil],
-
0x2493 => [0, 0, nil, "12.", nil, nil, nil],
-
0x2494 => [0, 0, nil, "13.", nil, nil, nil],
-
0x2495 => [0, 0, nil, "14.", nil, nil, nil],
-
0x2496 => [0, 0, nil, "15.", nil, nil, nil],
-
0x2497 => [0, 0, nil, "16.", nil, nil, nil],
-
0x2498 => [0, 0, nil, "17.", nil, nil, nil],
-
0x2499 => [0, 0, nil, "18.", nil, nil, nil],
-
0x249a => [0, 0, nil, "19.", nil, nil, nil],
-
0x249b => [0, 0, nil, "20.", nil, nil, nil],
-
0x249c => [0, 0, nil, "(a)", nil, nil, nil],
-
0x249d => [0, 0, nil, "(b)", nil, nil, nil],
-
0x249e => [0, 0, nil, "(c)", nil, nil, nil],
-
0x249f => [0, 0, nil, "(d)", nil, nil, nil],
-
0x24a0 => [0, 0, nil, "(e)", nil, nil, nil],
-
0x24a1 => [0, 0, nil, "(f)", nil, nil, nil],
-
0x24a2 => [0, 0, nil, "(g)", nil, nil, nil],
-
0x24a3 => [0, 0, nil, "(h)", nil, nil, nil],
-
0x24a4 => [0, 0, nil, "(i)", nil, nil, nil],
-
0x24a5 => [0, 0, nil, "(j)", nil, nil, nil],
-
0x24a6 => [0, 0, nil, "(k)", nil, nil, nil],
-
0x24a7 => [0, 0, nil, "(l)", nil, nil, nil],
-
0x24a8 => [0, 0, nil, "(m)", nil, nil, nil],
-
0x24a9 => [0, 0, nil, "(n)", nil, nil, nil],
-
0x24aa => [0, 0, nil, "(o)", nil, nil, nil],
-
0x24ab => [0, 0, nil, "(p)", nil, nil, nil],
-
0x24ac => [0, 0, nil, "(q)", nil, nil, nil],
-
0x24ad => [0, 0, nil, "(r)", nil, nil, nil],
-
0x24ae => [0, 0, nil, "(s)", nil, nil, nil],
-
0x24af => [0, 0, nil, "(t)", nil, nil, nil],
-
0x24b0 => [0, 0, nil, "(u)", nil, nil, nil],
-
0x24b1 => [0, 0, nil, "(v)", nil, nil, nil],
-
0x24b2 => [0, 0, nil, "(w)", nil, nil, nil],
-
0x24b3 => [0, 0, nil, "(x)", nil, nil, nil],
-
0x24b4 => [0, 0, nil, "(y)", nil, nil, nil],
-
0x24b5 => [0, 0, nil, "(z)", nil, nil, nil],
-
0x24b6 => [0, 0, nil, "A", nil, 0x24d0, nil],
-
0x24b7 => [0, 0, nil, "B", nil, 0x24d1, nil],
-
0x24b8 => [0, 0, nil, "C", nil, 0x24d2, nil],
-
0x24b9 => [0, 0, nil, "D", nil, 0x24d3, nil],
-
0x24ba => [0, 0, nil, "E", nil, 0x24d4, nil],
-
0x24bb => [0, 0, nil, "F", nil, 0x24d5, nil],
-
0x24bc => [0, 0, nil, "G", nil, 0x24d6, nil],
-
0x24bd => [0, 0, nil, "H", nil, 0x24d7, nil],
-
0x24be => [0, 0, nil, "I", nil, 0x24d8, nil],
-
0x24bf => [0, 0, nil, "J", nil, 0x24d9, nil],
-
0x24c0 => [0, 0, nil, "K", nil, 0x24da, nil],
-
0x24c1 => [0, 0, nil, "L", nil, 0x24db, nil],
-
0x24c2 => [0, 0, nil, "M", nil, 0x24dc, nil],
-
0x24c3 => [0, 0, nil, "N", nil, 0x24dd, nil],
-
0x24c4 => [0, 0, nil, "O", nil, 0x24de, nil],
-
0x24c5 => [0, 0, nil, "P", nil, 0x24df, nil],
-
0x24c6 => [0, 0, nil, "Q", nil, 0x24e0, nil],
-
0x24c7 => [0, 0, nil, "R", nil, 0x24e1, nil],
-
0x24c8 => [0, 0, nil, "S", nil, 0x24e2, nil],
-
0x24c9 => [0, 0, nil, "T", nil, 0x24e3, nil],
-
0x24ca => [0, 0, nil, "U", nil, 0x24e4, nil],
-
0x24cb => [0, 0, nil, "V", nil, 0x24e5, nil],
-
0x24cc => [0, 0, nil, "W", nil, 0x24e6, nil],
-
0x24cd => [0, 0, nil, "X", nil, 0x24e7, nil],
-
0x24ce => [0, 0, nil, "Y", nil, 0x24e8, nil],
-
0x24cf => [0, 0, nil, "Z", nil, 0x24e9, nil],
-
0x24d0 => [0, 0, nil, "a", 0x24b6, nil, 0x24b6],
-
0x24d1 => [0, 0, nil, "b", 0x24b7, nil, 0x24b7],
-
0x24d2 => [0, 0, nil, "c", 0x24b8, nil, 0x24b8],
-
0x24d3 => [0, 0, nil, "d", 0x24b9, nil, 0x24b9],
-
0x24d4 => [0, 0, nil, "e", 0x24ba, nil, 0x24ba],
-
0x24d5 => [0, 0, nil, "f", 0x24bb, nil, 0x24bb],
-
0x24d6 => [0, 0, nil, "g", 0x24bc, nil, 0x24bc],
-
0x24d7 => [0, 0, nil, "h", 0x24bd, nil, 0x24bd],
-
0x24d8 => [0, 0, nil, "i", 0x24be, nil, 0x24be],
-
0x24d9 => [0, 0, nil, "j", 0x24bf, nil, 0x24bf],
-
0x24da => [0, 0, nil, "k", 0x24c0, nil, 0x24c0],
-
0x24db => [0, 0, nil, "l", 0x24c1, nil, 0x24c1],
-
0x24dc => [0, 0, nil, "m", 0x24c2, nil, 0x24c2],
-
0x24dd => [0, 0, nil, "n", 0x24c3, nil, 0x24c3],
-
0x24de => [0, 0, nil, "o", 0x24c4, nil, 0x24c4],
-
0x24df => [0, 0, nil, "p", 0x24c5, nil, 0x24c5],
-
0x24e0 => [0, 0, nil, "q", 0x24c6, nil, 0x24c6],
-
0x24e1 => [0, 0, nil, "r", 0x24c7, nil, 0x24c7],
-
0x24e2 => [0, 0, nil, "s", 0x24c8, nil, 0x24c8],
-
0x24e3 => [0, 0, nil, "t", 0x24c9, nil, 0x24c9],
-
0x24e4 => [0, 0, nil, "u", 0x24ca, nil, 0x24ca],
-
0x24e5 => [0, 0, nil, "v", 0x24cb, nil, 0x24cb],
-
0x24e6 => [0, 0, nil, "w", 0x24cc, nil, 0x24cc],
-
0x24e7 => [0, 0, nil, "x", 0x24cd, nil, 0x24cd],
-
0x24e8 => [0, 0, nil, "y", 0x24ce, nil, 0x24ce],
-
0x24e9 => [0, 0, nil, "z", 0x24cf, nil, 0x24cf],
-
0x24ea => [0, 0, nil, "0", nil, nil, nil],
-
0x2e9f => [0, 0, nil, "\346\257\215", nil, nil, nil],
-
0x2ef3 => [0, 0, nil, "\351\276\237", nil, nil, nil],
-
0x2f00 => [0, 0, nil, "\344\270\200", nil, nil, nil],
-
0x2f01 => [0, 0, nil, "\344\270\250", nil, nil, nil],
-
0x2f02 => [0, 0, nil, "\344\270\266", nil, nil, nil],
-
0x2f03 => [0, 0, nil, "\344\270\277", nil, nil, nil],
-
0x2f04 => [0, 0, nil, "\344\271\231", nil, nil, nil],
-
0x2f05 => [0, 0, nil, "\344\272\205", nil, nil, nil],
-
0x2f06 => [0, 0, nil, "\344\272\214", nil, nil, nil],
-
0x2f07 => [0, 0, nil, "\344\272\240", nil, nil, nil],
-
0x2f08 => [0, 0, nil, "\344\272\272", nil, nil, nil],
-
0x2f09 => [0, 0, nil, "\345\204\277", nil, nil, nil],
-
0x2f0a => [0, 0, nil, "\345\205\245", nil, nil, nil],
-
0x2f0b => [0, 0, nil, "\345\205\253", nil, nil, nil],
-
0x2f0c => [0, 0, nil, "\345\206\202", nil, nil, nil],
-
0x2f0d => [0, 0, nil, "\345\206\226", nil, nil, nil],
-
0x2f0e => [0, 0, nil, "\345\206\253", nil, nil, nil],
-
0x2f0f => [0, 0, nil, "\345\207\240", nil, nil, nil],
-
0x2f10 => [0, 0, nil, "\345\207\265", nil, nil, nil],
-
0x2f11 => [0, 0, nil, "\345\210\200", nil, nil, nil],
-
0x2f12 => [0, 0, nil, "\345\212\233", nil, nil, nil],
-
0x2f13 => [0, 0, nil, "\345\213\271", nil, nil, nil],
-
0x2f14 => [0, 0, nil, "\345\214\225", nil, nil, nil],
-
0x2f15 => [0, 0, nil, "\345\214\232", nil, nil, nil],
-
0x2f16 => [0, 0, nil, "\345\214\270", nil, nil, nil],
-
0x2f17 => [0, 0, nil, "\345\215\201", nil, nil, nil],
-
0x2f18 => [0, 0, nil, "\345\215\234", nil, nil, nil],
-
0x2f19 => [0, 0, nil, "\345\215\251", nil, nil, nil],
-
0x2f1a => [0, 0, nil, "\345\216\202", nil, nil, nil],
-
0x2f1b => [0, 0, nil, "\345\216\266", nil, nil, nil],
-
0x2f1c => [0, 0, nil, "\345\217\210", nil, nil, nil],
-
0x2f1d => [0, 0, nil, "\345\217\243", nil, nil, nil],
-
0x2f1e => [0, 0, nil, "\345\233\227", nil, nil, nil],
-
0x2f1f => [0, 0, nil, "\345\234\237", nil, nil, nil],
-
0x2f20 => [0, 0, nil, "\345\243\253", nil, nil, nil],
-
0x2f21 => [0, 0, nil, "\345\244\202", nil, nil, nil],
-
0x2f22 => [0, 0, nil, "\345\244\212", nil, nil, nil],
-
0x2f23 => [0, 0, nil, "\345\244\225", nil, nil, nil],
-
0x2f24 => [0, 0, nil, "\345\244\247", nil, nil, nil],
-
0x2f25 => [0, 0, nil, "\345\245\263", nil, nil, nil],
-
0x2f26 => [0, 0, nil, "\345\255\220", nil, nil, nil],
-
0x2f27 => [0, 0, nil, "\345\256\200", nil, nil, nil],
-
0x2f28 => [0, 0, nil, "\345\257\270", nil, nil, nil],
-
0x2f29 => [0, 0, nil, "\345\260\217", nil, nil, nil],
-
0x2f2a => [0, 0, nil, "\345\260\242", nil, nil, nil],
-
0x2f2b => [0, 0, nil, "\345\260\270", nil, nil, nil],
-
0x2f2c => [0, 0, nil, "\345\261\256", nil, nil, nil],
-
0x2f2d => [0, 0, nil, "\345\261\261", nil, nil, nil],
-
0x2f2e => [0, 0, nil, "\345\267\233", nil, nil, nil],
-
0x2f2f => [0, 0, nil, "\345\267\245", nil, nil, nil],
-
0x2f30 => [0, 0, nil, "\345\267\261", nil, nil, nil],
-
0x2f31 => [0, 0, nil, "\345\267\276", nil, nil, nil],
-
0x2f32 => [0, 0, nil, "\345\271\262", nil, nil, nil],
-
0x2f33 => [0, 0, nil, "\345\271\272", nil, nil, nil],
-
0x2f34 => [0, 0, nil, "\345\271\277", nil, nil, nil],
-
0x2f35 => [0, 0, nil, "\345\273\264", nil, nil, nil],
-
0x2f36 => [0, 0, nil, "\345\273\276", nil, nil, nil],
-
0x2f37 => [0, 0, nil, "\345\274\213", nil, nil, nil],
-
0x2f38 => [0, 0, nil, "\345\274\223", nil, nil, nil],
-
0x2f39 => [0, 0, nil, "\345\275\220", nil, nil, nil],
-
0x2f3a => [0, 0, nil, "\345\275\241", nil, nil, nil],
-
0x2f3b => [0, 0, nil, "\345\275\263", nil, nil, nil],
-
0x2f3c => [0, 0, nil, "\345\277\203", nil, nil, nil],
-
0x2f3d => [0, 0, nil, "\346\210\210", nil, nil, nil],
-
0x2f3e => [0, 0, nil, "\346\210\266", nil, nil, nil],
-
0x2f3f => [0, 0, nil, "\346\211\213", nil, nil, nil],
-
0x2f40 => [0, 0, nil, "\346\224\257", nil, nil, nil],
-
0x2f41 => [0, 0, nil, "\346\224\264", nil, nil, nil],
-
0x2f42 => [0, 0, nil, "\346\226\207", nil, nil, nil],
-
0x2f43 => [0, 0, nil, "\346\226\227", nil, nil, nil],
-
0x2f44 => [0, 0, nil, "\346\226\244", nil, nil, nil],
-
0x2f45 => [0, 0, nil, "\346\226\271", nil, nil, nil],
-
0x2f46 => [0, 0, nil, "\346\227\240", nil, nil, nil],
-
0x2f47 => [0, 0, nil, "\346\227\245", nil, nil, nil],
-
0x2f48 => [0, 0, nil, "\346\233\260", nil, nil, nil],
-
0x2f49 => [0, 0, nil, "\346\234\210", nil, nil, nil],
-
0x2f4a => [0, 0, nil, "\346\234\250", nil, nil, nil],
-
0x2f4b => [0, 0, nil, "\346\254\240", nil, nil, nil],
-
0x2f4c => [0, 0, nil, "\346\255\242", nil, nil, nil],
-
0x2f4d => [0, 0, nil, "\346\255\271", nil, nil, nil],
-
0x2f4e => [0, 0, nil, "\346\256\263", nil, nil, nil],
-
0x2f4f => [0, 0, nil, "\346\257\213", nil, nil, nil],
-
0x2f50 => [0, 0, nil, "\346\257\224", nil, nil, nil],
-
0x2f51 => [0, 0, nil, "\346\257\233", nil, nil, nil],
-
0x2f52 => [0, 0, nil, "\346\260\217", nil, nil, nil],
-
0x2f53 => [0, 0, nil, "\346\260\224", nil, nil, nil],
-
0x2f54 => [0, 0, nil, "\346\260\264", nil, nil, nil],
-
0x2f55 => [0, 0, nil, "\347\201\253", nil, nil, nil],
-
0x2f56 => [0, 0, nil, "\347\210\252", nil, nil, nil],
-
0x2f57 => [0, 0, nil, "\347\210\266", nil, nil, nil],
-
0x2f58 => [0, 0, nil, "\347\210\273", nil, nil, nil],
-
0x2f59 => [0, 0, nil, "\347\210\277", nil, nil, nil],
-
0x2f5a => [0, 0, nil, "\347\211\207", nil, nil, nil],
-
0x2f5b => [0, 0, nil, "\347\211\231", nil, nil, nil],
-
0x2f5c => [0, 0, nil, "\347\211\233", nil, nil, nil],
-
0x2f5d => [0, 0, nil, "\347\212\254", nil, nil, nil],
-
0x2f5e => [0, 0, nil, "\347\216\204", nil, nil, nil],
-
0x2f5f => [0, 0, nil, "\347\216\211", nil, nil, nil],
-
0x2f60 => [0, 0, nil, "\347\223\234", nil, nil, nil],
-
0x2f61 => [0, 0, nil, "\347\223\246", nil, nil, nil],
-
0x2f62 => [0, 0, nil, "\347\224\230", nil, nil, nil],
-
0x2f63 => [0, 0, nil, "\347\224\237", nil, nil, nil],
-
0x2f64 => [0, 0, nil, "\347\224\250", nil, nil, nil],
-
0x2f65 => [0, 0, nil, "\347\224\260", nil, nil, nil],
-
0x2f66 => [0, 0, nil, "\347\226\213", nil, nil, nil],
-
0x2f67 => [0, 0, nil, "\347\226\222", nil, nil, nil],
-
0x2f68 => [0, 0, nil, "\347\231\266", nil, nil, nil],
-
0x2f69 => [0, 0, nil, "\347\231\275", nil, nil, nil],
-
0x2f6a => [0, 0, nil, "\347\232\256", nil, nil, nil],
-
0x2f6b => [0, 0, nil, "\347\232\277", nil, nil, nil],
-
0x2f6c => [0, 0, nil, "\347\233\256", nil, nil, nil],
-
0x2f6d => [0, 0, nil, "\347\237\233", nil, nil, nil],
-
0x2f6e => [0, 0, nil, "\347\237\242", nil, nil, nil],
-
0x2f6f => [0, 0, nil, "\347\237\263", nil, nil, nil],
-
0x2f70 => [0, 0, nil, "\347\244\272", nil, nil, nil],
-
0x2f71 => [0, 0, nil, "\347\246\270", nil, nil, nil],
-
0x2f72 => [0, 0, nil, "\347\246\276", nil, nil, nil],
-
0x2f73 => [0, 0, nil, "\347\251\264", nil, nil, nil],
-
0x2f74 => [0, 0, nil, "\347\253\213", nil, nil, nil],
-
0x2f75 => [0, 0, nil, "\347\253\271", nil, nil, nil],
-
0x2f76 => [0, 0, nil, "\347\261\263", nil, nil, nil],
-
0x2f77 => [0, 0, nil, "\347\263\270", nil, nil, nil],
-
0x2f78 => [0, 0, nil, "\347\274\266", nil, nil, nil],
-
0x2f79 => [0, 0, nil, "\347\275\221", nil, nil, nil],
-
0x2f7a => [0, 0, nil, "\347\276\212", nil, nil, nil],
-
0x2f7b => [0, 0, nil, "\347\276\275", nil, nil, nil],
-
0x2f7c => [0, 0, nil, "\350\200\201", nil, nil, nil],
-
0x2f7d => [0, 0, nil, "\350\200\214", nil, nil, nil],
-
0x2f7e => [0, 0, nil, "\350\200\222", nil, nil, nil],
-
0x2f7f => [0, 0, nil, "\350\200\263", nil, nil, nil],
-
0x2f80 => [0, 0, nil, "\350\201\277", nil, nil, nil],
-
0x2f81 => [0, 0, nil, "\350\202\211", nil, nil, nil],
-
0x2f82 => [0, 0, nil, "\350\207\243", nil, nil, nil],
-
0x2f83 => [0, 0, nil, "\350\207\252", nil, nil, nil],
-
0x2f84 => [0, 0, nil, "\350\207\263", nil, nil, nil],
-
0x2f85 => [0, 0, nil, "\350\207\274", nil, nil, nil],
-
0x2f86 => [0, 0, nil, "\350\210\214", nil, nil, nil],
-
0x2f87 => [0, 0, nil, "\350\210\233", nil, nil, nil],
-
0x2f88 => [0, 0, nil, "\350\210\237", nil, nil, nil],
-
0x2f89 => [0, 0, nil, "\350\211\256", nil, nil, nil],
-
0x2f8a => [0, 0, nil, "\350\211\262", nil, nil, nil],
-
0x2f8b => [0, 0, nil, "\350\211\270", nil, nil, nil],
-
0x2f8c => [0, 0, nil, "\350\231\215", nil, nil, nil],
-
0x2f8d => [0, 0, nil, "\350\231\253", nil, nil, nil],
-
0x2f8e => [0, 0, nil, "\350\241\200", nil, nil, nil],
-
0x2f8f => [0, 0, nil, "\350\241\214", nil, nil, nil],
-
0x2f90 => [0, 0, nil, "\350\241\243", nil, nil, nil],
-
0x2f91 => [0, 0, nil, "\350\245\276", nil, nil, nil],
-
0x2f92 => [0, 0, nil, "\350\246\213", nil, nil, nil],
-
0x2f93 => [0, 0, nil, "\350\247\222", nil, nil, nil],
-
0x2f94 => [0, 0, nil, "\350\250\200", nil, nil, nil],
-
0x2f95 => [0, 0, nil, "\350\260\267", nil, nil, nil],
-
0x2f96 => [0, 0, nil, "\350\261\206", nil, nil, nil],
-
0x2f97 => [0, 0, nil, "\350\261\225", nil, nil, nil],
-
0x2f98 => [0, 0, nil, "\350\261\270", nil, nil, nil],
-
0x2f99 => [0, 0, nil, "\350\262\235", nil, nil, nil],
-
0x2f9a => [0, 0, nil, "\350\265\244", nil, nil, nil],
-
0x2f9b => [0, 0, nil, "\350\265\260", nil, nil, nil],
-
0x2f9c => [0, 0, nil, "\350\266\263", nil, nil, nil],
-
0x2f9d => [0, 0, nil, "\350\272\253", nil, nil, nil],
-
0x2f9e => [0, 0, nil, "\350\273\212", nil, nil, nil],
-
0x2f9f => [0, 0, nil, "\350\276\233", nil, nil, nil],
-
0x2fa0 => [0, 0, nil, "\350\276\260", nil, nil, nil],
-
0x2fa1 => [0, 0, nil, "\350\276\265", nil, nil, nil],
-
0x2fa2 => [0, 0, nil, "\351\202\221", nil, nil, nil],
-
0x2fa3 => [0, 0, nil, "\351\205\211", nil, nil, nil],
-
0x2fa4 => [0, 0, nil, "\351\207\206", nil, nil, nil],
-
0x2fa5 => [0, 0, nil, "\351\207\214", nil, nil, nil],
-
0x2fa6 => [0, 0, nil, "\351\207\221", nil, nil, nil],
-
0x2fa7 => [0, 0, nil, "\351\225\267", nil, nil, nil],
-
0x2fa8 => [0, 0, nil, "\351\226\200", nil, nil, nil],
-
0x2fa9 => [0, 0, nil, "\351\230\234", nil, nil, nil],
-
0x2faa => [0, 0, nil, "\351\232\266", nil, nil, nil],
-
0x2fab => [0, 0, nil, "\351\232\271", nil, nil, nil],
-
0x2fac => [0, 0, nil, "\351\233\250", nil, nil, nil],
-
0x2fad => [0, 0, nil, "\351\235\221", nil, nil, nil],
-
0x2fae => [0, 0, nil, "\351\235\236", nil, nil, nil],
-
0x2faf => [0, 0, nil, "\351\235\242", nil, nil, nil],
-
0x2fb0 => [0, 0, nil, "\351\235\251", nil, nil, nil],
-
0x2fb1 => [0, 0, nil, "\351\237\213", nil, nil, nil],
-
0x2fb2 => [0, 0, nil, "\351\237\255", nil, nil, nil],
-
0x2fb3 => [0, 0, nil, "\351\237\263", nil, nil, nil],
-
0x2fb4 => [0, 0, nil, "\351\240\201", nil, nil, nil],
-
0x2fb5 => [0, 0, nil, "\351\242\250", nil, nil, nil],
-
0x2fb6 => [0, 0, nil, "\351\243\233", nil, nil, nil],
-
0x2fb7 => [0, 0, nil, "\351\243\237", nil, nil, nil],
-
0x2fb8 => [0, 0, nil, "\351\246\226", nil, nil, nil],
-
0x2fb9 => [0, 0, nil, "\351\246\231", nil, nil, nil],
-
0x2fba => [0, 0, nil, "\351\246\254", nil, nil, nil],
-
0x2fbb => [0, 0, nil, "\351\252\250", nil, nil, nil],
-
0x2fbc => [0, 0, nil, "\351\253\230", nil, nil, nil],
-
0x2fbd => [0, 0, nil, "\351\253\237", nil, nil, nil],
-
0x2fbe => [0, 0, nil, "\351\254\245", nil, nil, nil],
-
0x2fbf => [0, 0, nil, "\351\254\257", nil, nil, nil],
-
0x2fc0 => [0, 0, nil, "\351\254\262", nil, nil, nil],
-
0x2fc1 => [0, 0, nil, "\351\254\274", nil, nil, nil],
-
0x2fc2 => [0, 0, nil, "\351\255\232", nil, nil, nil],
-
0x2fc3 => [0, 0, nil, "\351\263\245", nil, nil, nil],
-
0x2fc4 => [0, 0, nil, "\351\271\265", nil, nil, nil],
-
0x2fc5 => [0, 0, nil, "\351\271\277", nil, nil, nil],
-
0x2fc6 => [0, 0, nil, "\351\272\245", nil, nil, nil],
-
0x2fc7 => [0, 0, nil, "\351\272\273", nil, nil, nil],
-
0x2fc8 => [0, 0, nil, "\351\273\203", nil, nil, nil],
-
0x2fc9 => [0, 0, nil, "\351\273\215", nil, nil, nil],
-
0x2fca => [0, 0, nil, "\351\273\221", nil, nil, nil],
-
0x2fcb => [0, 0, nil, "\351\273\271", nil, nil, nil],
-
0x2fcc => [0, 0, nil, "\351\273\275", nil, nil, nil],
-
0x2fcd => [0, 0, nil, "\351\274\216", nil, nil, nil],
-
0x2fce => [0, 0, nil, "\351\274\223", nil, nil, nil],
-
0x2fcf => [0, 0, nil, "\351\274\240", nil, nil, nil],
-
0x2fd0 => [0, 0, nil, "\351\274\273", nil, nil, nil],
-
0x2fd1 => [0, 0, nil, "\351\275\212", nil, nil, nil],
-
0x2fd2 => [0, 0, nil, "\351\275\222", nil, nil, nil],
-
0x2fd3 => [0, 0, nil, "\351\276\215", nil, nil, nil],
-
0x2fd4 => [0, 0, nil, "\351\276\234", nil, nil, nil],
-
0x2fd5 => [0, 0, nil, "\351\276\240", nil, nil, nil],
-
0x3000 => [0, 0, nil, " ", nil, nil, nil],
-
0x302a => [218, 0, nil, nil, nil, nil, nil],
-
0x302b => [228, 0, nil, nil, nil, nil, nil],
-
0x302c => [232, 0, nil, nil, nil, nil, nil],
-
0x302d => [222, 0, nil, nil, nil, nil, nil],
-
0x302e => [224, 0, nil, nil, nil, nil, nil],
-
0x302f => [224, 0, nil, nil, nil, nil, nil],
-
0x3036 => [0, 0, nil, "\343\200\222", nil, nil, nil],
-
0x3038 => [0, 0, nil, "\345\215\201", nil, nil, nil],
-
0x3039 => [0, 0, nil, "\345\215\204", nil, nil, nil],
-
0x303a => [0, 0, nil, "\345\215\205", nil, nil, nil],
-
0x304c => [0, 0, "\343\201\213\343\202\231", "\343\201\213\343\202\231", nil, nil, nil],
-
0x304e => [0, 0, "\343\201\215\343\202\231", "\343\201\215\343\202\231", nil, nil, nil],
-
0x3050 => [0, 0, "\343\201\217\343\202\231", "\343\201\217\343\202\231", nil, nil, nil],
-
0x3052 => [0, 0, "\343\201\221\343\202\231", "\343\201\221\343\202\231", nil, nil, nil],
-
0x3054 => [0, 0, "\343\201\223\343\202\231", "\343\201\223\343\202\231", nil, nil, nil],
-
0x3056 => [0, 0, "\343\201\225\343\202\231", "\343\201\225\343\202\231", nil, nil, nil],
-
0x3058 => [0, 0, "\343\201\227\343\202\231", "\343\201\227\343\202\231", nil, nil, nil],
-
0x305a => [0, 0, "\343\201\231\343\202\231", "\343\201\231\343\202\231", nil, nil, nil],
-
0x305c => [0, 0, "\343\201\233\343\202\231", "\343\201\233\343\202\231", nil, nil, nil],
-
0x305e => [0, 0, "\343\201\235\343\202\231", "\343\201\235\343\202\231", nil, nil, nil],
-
0x3060 => [0, 0, "\343\201\237\343\202\231", "\343\201\237\343\202\231", nil, nil, nil],
-
0x3062 => [0, 0, "\343\201\241\343\202\231", "\343\201\241\343\202\231", nil, nil, nil],
-
0x3065 => [0, 0, "\343\201\244\343\202\231", "\343\201\244\343\202\231", nil, nil, nil],
-
0x3067 => [0, 0, "\343\201\246\343\202\231", "\343\201\246\343\202\231", nil, nil, nil],
-
0x3069 => [0, 0, "\343\201\250\343\202\231", "\343\201\250\343\202\231", nil, nil, nil],
-
0x3070 => [0, 0, "\343\201\257\343\202\231", "\343\201\257\343\202\231", nil, nil, nil],
-
0x3071 => [0, 0, "\343\201\257\343\202\232", "\343\201\257\343\202\232", nil, nil, nil],
-
0x3073 => [0, 0, "\343\201\262\343\202\231", "\343\201\262\343\202\231", nil, nil, nil],
-
0x3074 => [0, 0, "\343\201\262\343\202\232", "\343\201\262\343\202\232", nil, nil, nil],
-
0x3076 => [0, 0, "\343\201\265\343\202\231", "\343\201\265\343\202\231", nil, nil, nil],
-
0x3077 => [0, 0, "\343\201\265\343\202\232", "\343\201\265\343\202\232", nil, nil, nil],
-
0x3079 => [0, 0, "\343\201\270\343\202\231", "\343\201\270\343\202\231", nil, nil, nil],
-
0x307a => [0, 0, "\343\201\270\343\202\232", "\343\201\270\343\202\232", nil, nil, nil],
-
0x307c => [0, 0, "\343\201\273\343\202\231", "\343\201\273\343\202\231", nil, nil, nil],
-
0x307d => [0, 0, "\343\201\273\343\202\232", "\343\201\273\343\202\232", nil, nil, nil],
-
0x3094 => [0, 0, "\343\201\206\343\202\231", "\343\201\206\343\202\231", nil, nil, nil],
-
0x3099 => [8, 0, nil, nil, nil, nil, nil],
-
0x309a => [8, 0, nil, nil, nil, nil, nil],
-
0x309b => [0, 0, nil, " \343\202\231", nil, nil, nil],
-
0x309c => [0, 0, nil, " \343\202\232", nil, nil, nil],
-
0x309e => [0, 0, "\343\202\235\343\202\231", "\343\202\235\343\202\231", nil, nil, nil],
-
0x30ac => [0, 0, "\343\202\253\343\202\231", "\343\202\253\343\202\231", nil, nil, nil],
-
0x30ae => [0, 0, "\343\202\255\343\202\231", "\343\202\255\343\202\231", nil, nil, nil],
-
0x30b0 => [0, 0, "\343\202\257\343\202\231", "\343\202\257\343\202\231", nil, nil, nil],
-
0x30b2 => [0, 0, "\343\202\261\343\202\231", "\343\202\261\343\202\231", nil, nil, nil],
-
0x30b4 => [0, 0, "\343\202\263\343\202\231", "\343\202\263\343\202\231", nil, nil, nil],
-
0x30b6 => [0, 0, "\343\202\265\343\202\231", "\343\202\265\343\202\231", nil, nil, nil],
-
0x30b8 => [0, 0, "\343\202\267\343\202\231", "\343\202\267\343\202\231", nil, nil, nil],
-
0x30ba => [0, 0, "\343\202\271\343\202\231", "\343\202\271\343\202\231", nil, nil, nil],
-
0x30bc => [0, 0, "\343\202\273\343\202\231", "\343\202\273\343\202\231", nil, nil, nil],
-
0x30be => [0, 0, "\343\202\275\343\202\231", "\343\202\275\343\202\231", nil, nil, nil],
-
0x30c0 => [0, 0, "\343\202\277\343\202\231", "\343\202\277\343\202\231", nil, nil, nil],
-
0x30c2 => [0, 0, "\343\203\201\343\202\231", "\343\203\201\343\202\231", nil, nil, nil],
-
0x30c5 => [0, 0, "\343\203\204\343\202\231", "\343\203\204\343\202\231", nil, nil, nil],
-
0x30c7 => [0, 0, "\343\203\206\343\202\231", "\343\203\206\343\202\231", nil, nil, nil],
-
0x30c9 => [0, 0, "\343\203\210\343\202\231", "\343\203\210\343\202\231", nil, nil, nil],
-
0x30d0 => [0, 0, "\343\203\217\343\202\231", "\343\203\217\343\202\231", nil, nil, nil],
-
0x30d1 => [0, 0, "\343\203\217\343\202\232", "\343\203\217\343\202\232", nil, nil, nil],
-
0x30d3 => [0, 0, "\343\203\222\343\202\231", "\343\203\222\343\202\231", nil, nil, nil],
-
0x30d4 => [0, 0, "\343\203\222\343\202\232", "\343\203\222\343\202\232", nil, nil, nil],
-
0x30d6 => [0, 0, "\343\203\225\343\202\231", "\343\203\225\343\202\231", nil, nil, nil],
-
0x30d7 => [0, 0, "\343\203\225\343\202\232", "\343\203\225\343\202\232", nil, nil, nil],
-
0x30d9 => [0, 0, "\343\203\230\343\202\231", "\343\203\230\343\202\231", nil, nil, nil],
-
0x30da => [0, 0, "\343\203\230\343\202\232", "\343\203\230\343\202\232", nil, nil, nil],
-
0x30dc => [0, 0, "\343\203\233\343\202\231", "\343\203\233\343\202\231", nil, nil, nil],
-
0x30dd => [0, 0, "\343\203\233\343\202\232", "\343\203\233\343\202\232", nil, nil, nil],
-
0x30f4 => [0, 0, "\343\202\246\343\202\231", "\343\202\246\343\202\231", nil, nil, nil],
-
0x30f7 => [0, 0, "\343\203\257\343\202\231", "\343\203\257\343\202\231", nil, nil, nil],
-
0x30f8 => [0, 0, "\343\203\260\343\202\231", "\343\203\260\343\202\231", nil, nil, nil],
-
0x30f9 => [0, 0, "\343\203\261\343\202\231", "\343\203\261\343\202\231", nil, nil, nil],
-
0x30fa => [0, 0, "\343\203\262\343\202\231", "\343\203\262\343\202\231", nil, nil, nil],
-
0x30fe => [0, 0, "\343\203\275\343\202\231", "\343\203\275\343\202\231", nil, nil, nil],
-
0x3131 => [0, 0, nil, "\341\204\200", nil, nil, nil],
-
0x3132 => [0, 0, nil, "\341\204\201", nil, nil, nil],
-
0x3133 => [0, 0, nil, "\341\206\252", nil, nil, nil],
-
0x3134 => [0, 0, nil, "\341\204\202", nil, nil, nil],
-
0x3135 => [0, 0, nil, "\341\206\254", nil, nil, nil],
-
0x3136 => [0, 0, nil, "\341\206\255", nil, nil, nil],
-
0x3137 => [0, 0, nil, "\341\204\203", nil, nil, nil],
-
0x3138 => [0, 0, nil, "\341\204\204", nil, nil, nil],
-
0x3139 => [0, 0, nil, "\341\204\205", nil, nil, nil],
-
0x313a => [0, 0, nil, "\341\206\260", nil, nil, nil],
-
0x313b => [0, 0, nil, "\341\206\261", nil, nil, nil],
-
0x313c => [0, 0, nil, "\341\206\262", nil, nil, nil],
-
0x313d => [0, 0, nil, "\341\206\263", nil, nil, nil],
-
0x313e => [0, 0, nil, "\341\206\264", nil, nil, nil],
-
0x313f => [0, 0, nil, "\341\206\265", nil, nil, nil],
-
0x3140 => [0, 0, nil, "\341\204\232", nil, nil, nil],
-
0x3141 => [0, 0, nil, "\341\204\206", nil, nil, nil],
-
0x3142 => [0, 0, nil, "\341\204\207", nil, nil, nil],
-
0x3143 => [0, 0, nil, "\341\204\210", nil, nil, nil],
-
0x3144 => [0, 0, nil, "\341\204\241", nil, nil, nil],
-
0x3145 => [0, 0, nil, "\341\204\211", nil, nil, nil],
-
0x3146 => [0, 0, nil, "\341\204\212", nil, nil, nil],
-
0x3147 => [0, 0, nil, "\341\204\213", nil, nil, nil],
-
0x3148 => [0, 0, nil, "\341\204\214", nil, nil, nil],
-
0x3149 => [0, 0, nil, "\341\204\215", nil, nil, nil],
-
0x314a => [0, 0, nil, "\341\204\216", nil, nil, nil],
-
0x314b => [0, 0, nil, "\341\204\217", nil, nil, nil],
-
0x314c => [0, 0, nil, "\341\204\220", nil, nil, nil],
-
0x314d => [0, 0, nil, "\341\204\221", nil, nil, nil],
-
0x314e => [0, 0, nil, "\341\204\222", nil, nil, nil],
-
0x314f => [0, 0, nil, "\341\205\241", nil, nil, nil],
-
0x3150 => [0, 0, nil, "\341\205\242", nil, nil, nil],
-
0x3151 => [0, 0, nil, "\341\205\243", nil, nil, nil],
-
0x3152 => [0, 0, nil, "\341\205\244", nil, nil, nil],
-
0x3153 => [0, 0, nil, "\341\205\245", nil, nil, nil],
-
0x3154 => [0, 0, nil, "\341\205\246", nil, nil, nil],
-
0x3155 => [0, 0, nil, "\341\205\247", nil, nil, nil],
-
0x3156 => [0, 0, nil, "\341\205\250", nil, nil, nil],
-
0x3157 => [0, 0, nil, "\341\205\251", nil, nil, nil],
-
0x3158 => [0, 0, nil, "\341\205\252", nil, nil, nil],
-
0x3159 => [0, 0, nil, "\341\205\253", nil, nil, nil],
-
0x315a => [0, 0, nil, "\341\205\254", nil, nil, nil],
-
0x315b => [0, 0, nil, "\341\205\255", nil, nil, nil],
-
0x315c => [0, 0, nil, "\341\205\256", nil, nil, nil],
-
0x315d => [0, 0, nil, "\341\205\257", nil, nil, nil],
-
0x315e => [0, 0, nil, "\341\205\260", nil, nil, nil],
-
0x315f => [0, 0, nil, "\341\205\261", nil, nil, nil],
-
0x3160 => [0, 0, nil, "\341\205\262", nil, nil, nil],
-
0x3161 => [0, 0, nil, "\341\205\263", nil, nil, nil],
-
0x3162 => [0, 0, nil, "\341\205\264", nil, nil, nil],
-
0x3163 => [0, 0, nil, "\341\205\265", nil, nil, nil],
-
0x3164 => [0, 0, nil, "\341\205\240", nil, nil, nil],
-
0x3165 => [0, 0, nil, "\341\204\224", nil, nil, nil],
-
0x3166 => [0, 0, nil, "\341\204\225", nil, nil, nil],
-
0x3167 => [0, 0, nil, "\341\207\207", nil, nil, nil],
-
0x3168 => [0, 0, nil, "\341\207\210", nil, nil, nil],
-
0x3169 => [0, 0, nil, "\341\207\214", nil, nil, nil],
-
0x316a => [0, 0, nil, "\341\207\216", nil, nil, nil],
-
0x316b => [0, 0, nil, "\341\207\223", nil, nil, nil],
-
0x316c => [0, 0, nil, "\341\207\227", nil, nil, nil],
-
0x316d => [0, 0, nil, "\341\207\231", nil, nil, nil],
-
0x316e => [0, 0, nil, "\341\204\234", nil, nil, nil],
-
0x316f => [0, 0, nil, "\341\207\235", nil, nil, nil],
-
0x3170 => [0, 0, nil, "\341\207\237", nil, nil, nil],
-
0x3171 => [0, 0, nil, "\341\204\235", nil, nil, nil],
-
0x3172 => [0, 0, nil, "\341\204\236", nil, nil, nil],
-
0x3173 => [0, 0, nil, "\341\204\240", nil, nil, nil],
-
0x3174 => [0, 0, nil, "\341\204\242", nil, nil, nil],
-
0x3175 => [0, 0, nil, "\341\204\243", nil, nil, nil],
-
0x3176 => [0, 0, nil, "\341\204\247", nil, nil, nil],
-
0x3177 => [0, 0, nil, "\341\204\251", nil, nil, nil],
-
0x3178 => [0, 0, nil, "\341\204\253", nil, nil, nil],
-
0x3179 => [0, 0, nil, "\341\204\254", nil, nil, nil],
-
0x317a => [0, 0, nil, "\341\204\255", nil, nil, nil],
-
0x317b => [0, 0, nil, "\341\204\256", nil, nil, nil],
-
0x317c => [0, 0, nil, "\341\204\257", nil, nil, nil],
-
0x317d => [0, 0, nil, "\341\204\262", nil, nil, nil],
-
0x317e => [0, 0, nil, "\341\204\266", nil, nil, nil],
-
0x317f => [0, 0, nil, "\341\205\200", nil, nil, nil],
-
0x3180 => [0, 0, nil, "\341\205\207", nil, nil, nil],
-
0x3181 => [0, 0, nil, "\341\205\214", nil, nil, nil],
-
0x3182 => [0, 0, nil, "\341\207\261", nil, nil, nil],
-
0x3183 => [0, 0, nil, "\341\207\262", nil, nil, nil],
-
0x3184 => [0, 0, nil, "\341\205\227", nil, nil, nil],
-
0x3185 => [0, 0, nil, "\341\205\230", nil, nil, nil],
-
0x3186 => [0, 0, nil, "\341\205\231", nil, nil, nil],
-
0x3187 => [0, 0, nil, "\341\206\204", nil, nil, nil],
-
0x3188 => [0, 0, nil, "\341\206\205", nil, nil, nil],
-
0x3189 => [0, 0, nil, "\341\206\210", nil, nil, nil],
-
0x318a => [0, 0, nil, "\341\206\221", nil, nil, nil],
-
0x318b => [0, 0, nil, "\341\206\222", nil, nil, nil],
-
0x318c => [0, 0, nil, "\341\206\224", nil, nil, nil],
-
0x318d => [0, 0, nil, "\341\206\236", nil, nil, nil],
-
0x318e => [0, 0, nil, "\341\206\241", nil, nil, nil],
-
0x3192 => [0, 0, nil, "\344\270\200", nil, nil, nil],
-
0x3193 => [0, 0, nil, "\344\272\214", nil, nil, nil],
-
0x3194 => [0, 0, nil, "\344\270\211", nil, nil, nil],
-
0x3195 => [0, 0, nil, "\345\233\233", nil, nil, nil],
-
0x3196 => [0, 0, nil, "\344\270\212", nil, nil, nil],
-
0x3197 => [0, 0, nil, "\344\270\255", nil, nil, nil],
-
0x3198 => [0, 0, nil, "\344\270\213", nil, nil, nil],
-
0x3199 => [0, 0, nil, "\347\224\262", nil, nil, nil],
-
0x319a => [0, 0, nil, "\344\271\231", nil, nil, nil],
-
0x319b => [0, 0, nil, "\344\270\231", nil, nil, nil],
-
0x319c => [0, 0, nil, "\344\270\201", nil, nil, nil],
-
0x319d => [0, 0, nil, "\345\244\251", nil, nil, nil],
-
0x319e => [0, 0, nil, "\345\234\260", nil, nil, nil],
-
0x319f => [0, 0, nil, "\344\272\272", nil, nil, nil],
-
0x3200 => [0, 0, nil, "(\341\204\200)", nil, nil, nil],
-
0x3201 => [0, 0, nil, "(\341\204\202)", nil, nil, nil],
-
0x3202 => [0, 0, nil, "(\341\204\203)", nil, nil, nil],
-
0x3203 => [0, 0, nil, "(\341\204\205)", nil, nil, nil],
-
0x3204 => [0, 0, nil, "(\341\204\206)", nil, nil, nil],
-
0x3205 => [0, 0, nil, "(\341\204\207)", nil, nil, nil],
-
0x3206 => [0, 0, nil, "(\341\204\211)", nil, nil, nil],
-
0x3207 => [0, 0, nil, "(\341\204\213)", nil, nil, nil],
-
0x3208 => [0, 0, nil, "(\341\204\214)", nil, nil, nil],
-
0x3209 => [0, 0, nil, "(\341\204\216)", nil, nil, nil],
-
0x320a => [0, 0, nil, "(\341\204\217)", nil, nil, nil],
-
0x320b => [0, 0, nil, "(\341\204\220)", nil, nil, nil],
-
0x320c => [0, 0, nil, "(\341\204\221)", nil, nil, nil],
-
0x320d => [0, 0, nil, "(\341\204\222)", nil, nil, nil],
-
0x320e => [0, 0, nil, "(\341\204\200\341\205\241)", nil, nil, nil],
-
0x320f => [0, 0, nil, "(\341\204\202\341\205\241)", nil, nil, nil],
-
0x3210 => [0, 0, nil, "(\341\204\203\341\205\241)", nil, nil, nil],
-
0x3211 => [0, 0, nil, "(\341\204\205\341\205\241)", nil, nil, nil],
-
0x3212 => [0, 0, nil, "(\341\204\206\341\205\241)", nil, nil, nil],
-
0x3213 => [0, 0, nil, "(\341\204\207\341\205\241)", nil, nil, nil],
-
0x3214 => [0, 0, nil, "(\341\204\211\341\205\241)", nil, nil, nil],
-
0x3215 => [0, 0, nil, "(\341\204\213\341\205\241)", nil, nil, nil],
-
0x3216 => [0, 0, nil, "(\341\204\214\341\205\241)", nil, nil, nil],
-
0x3217 => [0, 0, nil, "(\341\204\216\341\205\241)", nil, nil, nil],
-
0x3218 => [0, 0, nil, "(\341\204\217\341\205\241)", nil, nil, nil],
-
0x3219 => [0, 0, nil, "(\341\204\220\341\205\241)", nil, nil, nil],
-
0x321a => [0, 0, nil, "(\341\204\221\341\205\241)", nil, nil, nil],
-
0x321b => [0, 0, nil, "(\341\204\222\341\205\241)", nil, nil, nil],
-
0x321c => [0, 0, nil, "(\341\204\214\341\205\256)", nil, nil, nil],
-
0x3220 => [0, 0, nil, "(\344\270\200)", nil, nil, nil],
-
0x3221 => [0, 0, nil, "(\344\272\214)", nil, nil, nil],
-
0x3222 => [0, 0, nil, "(\344\270\211)", nil, nil, nil],
-
0x3223 => [0, 0, nil, "(\345\233\233)", nil, nil, nil],
-
0x3224 => [0, 0, nil, "(\344\272\224)", nil, nil, nil],
-
0x3225 => [0, 0, nil, "(\345\205\255)", nil, nil, nil],
-
0x3226 => [0, 0, nil, "(\344\270\203)", nil, nil, nil],
-
0x3227 => [0, 0, nil, "(\345\205\253)", nil, nil, nil],
-
0x3228 => [0, 0, nil, "(\344\271\235)", nil, nil, nil],
-
0x3229 => [0, 0, nil, "(\345\215\201)", nil, nil, nil],
-
0x322a => [0, 0, nil, "(\346\234\210)", nil, nil, nil],
-
0x322b => [0, 0, nil, "(\347\201\253)", nil, nil, nil],
-
0x322c => [0, 0, nil, "(\346\260\264)", nil, nil, nil],
-
0x322d => [0, 0, nil, "(\346\234\250)", nil, nil, nil],
-
0x322e => [0, 0, nil, "(\351\207\221)", nil, nil, nil],
-
0x322f => [0, 0, nil, "(\345\234\237)", nil, nil, nil],
-
0x3230 => [0, 0, nil, "(\346\227\245)", nil, nil, nil],
-
0x3231 => [0, 0, nil, "(\346\240\252)", nil, nil, nil],
-
0x3232 => [0, 0, nil, "(\346\234\211)", nil, nil, nil],
-
0x3233 => [0, 0, nil, "(\347\244\276)", nil, nil, nil],
-
0x3234 => [0, 0, nil, "(\345\220\215)", nil, nil, nil],
-
0x3235 => [0, 0, nil, "(\347\211\271)", nil, nil, nil],
-
0x3236 => [0, 0, nil, "(\350\262\241)", nil, nil, nil],
-
0x3237 => [0, 0, nil, "(\347\245\235)", nil, nil, nil],
-
0x3238 => [0, 0, nil, "(\345\212\264)", nil, nil, nil],
-
0x3239 => [0, 0, nil, "(\344\273\243)", nil, nil, nil],
-
0x323a => [0, 0, nil, "(\345\221\274)", nil, nil, nil],
-
0x323b => [0, 0, nil, "(\345\255\246)", nil, nil, nil],
-
0x323c => [0, 0, nil, "(\347\233\243)", nil, nil, nil],
-
0x323d => [0, 0, nil, "(\344\274\201)", nil, nil, nil],
-
0x323e => [0, 0, nil, "(\350\263\207)", nil, nil, nil],
-
0x323f => [0, 0, nil, "(\345\215\224)", nil, nil, nil],
-
0x3240 => [0, 0, nil, "(\347\245\255)", nil, nil, nil],
-
0x3241 => [0, 0, nil, "(\344\274\221)", nil, nil, nil],
-
0x3242 => [0, 0, nil, "(\350\207\252)", nil, nil, nil],
-
0x3243 => [0, 0, nil, "(\350\207\263)", nil, nil, nil],
-
0x3260 => [0, 0, nil, "\341\204\200", nil, nil, nil],
-
0x3261 => [0, 0, nil, "\341\204\202", nil, nil, nil],
-
0x3262 => [0, 0, nil, "\341\204\203", nil, nil, nil],
-
0x3263 => [0, 0, nil, "\341\204\205", nil, nil, nil],
-
0x3264 => [0, 0, nil, "\341\204\206", nil, nil, nil],
-
0x3265 => [0, 0, nil, "\341\204\207", nil, nil, nil],
-
0x3266 => [0, 0, nil, "\341\204\211", nil, nil, nil],
-
0x3267 => [0, 0, nil, "\341\204\213", nil, nil, nil],
-
0x3268 => [0, 0, nil, "\341\204\214", nil, nil, nil],
-
0x3269 => [0, 0, nil, "\341\204\216", nil, nil, nil],
-
0x326a => [0, 0, nil, "\341\204\217", nil, nil, nil],
-
0x326b => [0, 0, nil, "\341\204\220", nil, nil, nil],
-
0x326c => [0, 0, nil, "\341\204\221", nil, nil, nil],
-
0x326d => [0, 0, nil, "\341\204\222", nil, nil, nil],
-
0x326e => [0, 0, nil, "\341\204\200\341\205\241", nil, nil, nil],
-
0x326f => [0, 0, nil, "\341\204\202\341\205\241", nil, nil, nil],
-
0x3270 => [0, 0, nil, "\341\204\203\341\205\241", nil, nil, nil],
-
0x3271 => [0, 0, nil, "\341\204\205\341\205\241", nil, nil, nil],
-
0x3272 => [0, 0, nil, "\341\204\206\341\205\241", nil, nil, nil],
-
0x3273 => [0, 0, nil, "\341\204\207\341\205\241", nil, nil, nil],
-
0x3274 => [0, 0, nil, "\341\204\211\341\205\241", nil, nil, nil],
-
0x3275 => [0, 0, nil, "\341\204\213\341\205\241", nil, nil, nil],
-
0x3276 => [0, 0, nil, "\341\204\214\341\205\241", nil, nil, nil],
-
0x3277 => [0, 0, nil, "\341\204\216\341\205\241", nil, nil, nil],
-
0x3278 => [0, 0, nil, "\341\204\217\341\205\241", nil, nil, nil],
-
0x3279 => [0, 0, nil, "\341\204\220\341\205\241", nil, nil, nil],
-
0x327a => [0, 0, nil, "\341\204\221\341\205\241", nil, nil, nil],
-
0x327b => [0, 0, nil, "\341\204\222\341\205\241", nil, nil, nil],
-
0x3280 => [0, 0, nil, "\344\270\200", nil, nil, nil],
-
0x3281 => [0, 0, nil, "\344\272\214", nil, nil, nil],
-
0x3282 => [0, 0, nil, "\344\270\211", nil, nil, nil],
-
0x3283 => [0, 0, nil, "\345\233\233", nil, nil, nil],
-
0x3284 => [0, 0, nil, "\344\272\224", nil, nil, nil],
-
0x3285 => [0, 0, nil, "\345\205\255", nil, nil, nil],
-
0x3286 => [0, 0, nil, "\344\270\203", nil, nil, nil],
-
0x3287 => [0, 0, nil, "\345\205\253", nil, nil, nil],
-
0x3288 => [0, 0, nil, "\344\271\235", nil, nil, nil],
-
0x3289 => [0, 0, nil, "\345\215\201", nil, nil, nil],
-
0x328a => [0, 0, nil, "\346\234\210", nil, nil, nil],
-
0x328b => [0, 0, nil, "\347\201\253", nil, nil, nil],
-
0x328c => [0, 0, nil, "\346\260\264", nil, nil, nil],
-
0x328d => [0, 0, nil, "\346\234\250", nil, nil, nil],
-
0x328e => [0, 0, nil, "\351\207\221", nil, nil, nil],
-
0x328f => [0, 0, nil, "\345\234\237", nil, nil, nil],
-
0x3290 => [0, 0, nil, "\346\227\245", nil, nil, nil],
-
0x3291 => [0, 0, nil, "\346\240\252", nil, nil, nil],
-
0x3292 => [0, 0, nil, "\346\234\211", nil, nil, nil],
-
0x3293 => [0, 0, nil, "\347\244\276", nil, nil, nil],
-
0x3294 => [0, 0, nil, "\345\220\215", nil, nil, nil],
-
0x3295 => [0, 0, nil, "\347\211\271", nil, nil, nil],
-
0x3296 => [0, 0, nil, "\350\262\241", nil, nil, nil],
-
0x3297 => [0, 0, nil, "\347\245\235", nil, nil, nil],
-
0x3298 => [0, 0, nil, "\345\212\264", nil, nil, nil],
-
0x3299 => [0, 0, nil, "\347\247\230", nil, nil, nil],
-
0x329a => [0, 0, nil, "\347\224\267", nil, nil, nil],
-
0x329b => [0, 0, nil, "\345\245\263", nil, nil, nil],
-
0x329c => [0, 0, nil, "\351\201\251", nil, nil, nil],
-
0x329d => [0, 0, nil, "\345\204\252", nil, nil, nil],
-
0x329e => [0, 0, nil, "\345\215\260", nil, nil, nil],
-
0x329f => [0, 0, nil, "\346\263\250", nil, nil, nil],
-
0x32a0 => [0, 0, nil, "\351\240\205", nil, nil, nil],
-
0x32a1 => [0, 0, nil, "\344\274\221", nil, nil, nil],
-
0x32a2 => [0, 0, nil, "\345\206\231", nil, nil, nil],
-
0x32a3 => [0, 0, nil, "\346\255\243", nil, nil, nil],
-
0x32a4 => [0, 0, nil, "\344\270\212", nil, nil, nil],
-
0x32a5 => [0, 0, nil, "\344\270\255", nil, nil, nil],
-
0x32a6 => [0, 0, nil, "\344\270\213", nil, nil, nil],
-
0x32a7 => [0, 0, nil, "\345\267\246", nil, nil, nil],
-
0x32a8 => [0, 0, nil, "\345\217\263", nil, nil, nil],
-
0x32a9 => [0, 0, nil, "\345\214\273", nil, nil, nil],
-
0x32aa => [0, 0, nil, "\345\256\227", nil, nil, nil],
-
0x32ab => [0, 0, nil, "\345\255\246", nil, nil, nil],
-
0x32ac => [0, 0, nil, "\347\233\243", nil, nil, nil],
-
0x32ad => [0, 0, nil, "\344\274\201", nil, nil, nil],
-
0x32ae => [0, 0, nil, "\350\263\207", nil, nil, nil],
-
0x32af => [0, 0, nil, "\345\215\224", nil, nil, nil],
-
0x32b0 => [0, 0, nil, "\345\244\234", nil, nil, nil],
-
0x32c0 => [0, 0, nil, "1\346\234\210", nil, nil, nil],
-
0x32c1 => [0, 0, nil, "2\346\234\210", nil, nil, nil],
-
0x32c2 => [0, 0, nil, "3\346\234\210", nil, nil, nil],
-
0x32c3 => [0, 0, nil, "4\346\234\210", nil, nil, nil],
-
0x32c4 => [0, 0, nil, "5\346\234\210", nil, nil, nil],
-
0x32c5 => [0, 0, nil, "6\346\234\210", nil, nil, nil],
-
0x32c6 => [0, 0, nil, "7\346\234\210", nil, nil, nil],
-
0x32c7 => [0, 0, nil, "8\346\234\210", nil, nil, nil],
-
0x32c8 => [0, 0, nil, "9\346\234\210", nil, nil, nil],
-
0x32c9 => [0, 0, nil, "10\346\234\210", nil, nil, nil],
-
0x32ca => [0, 0, nil, "11\346\234\210", nil, nil, nil],
-
0x32cb => [0, 0, nil, "12\346\234\210", nil, nil, nil],
-
0x32d0 => [0, 0, nil, "\343\202\242", nil, nil, nil],
-
0x32d1 => [0, 0, nil, "\343\202\244", nil, nil, nil],
-
0x32d2 => [0, 0, nil, "\343\202\246", nil, nil, nil],
-
0x32d3 => [0, 0, nil, "\343\202\250", nil, nil, nil],
-
0x32d4 => [0, 0, nil, "\343\202\252", nil, nil, nil],
-
0x32d5 => [0, 0, nil, "\343\202\253", nil, nil, nil],
-
0x32d6 => [0, 0, nil, "\343\202\255", nil, nil, nil],
-
0x32d7 => [0, 0, nil, "\343\202\257", nil, nil, nil],
-
0x32d8 => [0, 0, nil, "\343\202\261", nil, nil, nil],
-
0x32d9 => [0, 0, nil, "\343\202\263", nil, nil, nil],
-
0x32da => [0, 0, nil, "\343\202\265", nil, nil, nil],
-
0x32db => [0, 0, nil, "\343\202\267", nil, nil, nil],
-
0x32dc => [0, 0, nil, "\343\202\271", nil, nil, nil],
-
0x32dd => [0, 0, nil, "\343\202\273", nil, nil, nil],
-
0x32de => [0, 0, nil, "\343\202\275", nil, nil, nil],
-
0x32df => [0, 0, nil, "\343\202\277", nil, nil, nil],
-
0x32e0 => [0, 0, nil, "\343\203\201", nil, nil, nil],
-
0x32e1 => [0, 0, nil, "\343\203\204", nil, nil, nil],
-
0x32e2 => [0, 0, nil, "\343\203\206", nil, nil, nil],
-
0x32e3 => [0, 0, nil, "\343\203\210", nil, nil, nil],
-
0x32e4 => [0, 0, nil, "\343\203\212", nil, nil, nil],
-
0x32e5 => [0, 0, nil, "\343\203\213", nil, nil, nil],
-
0x32e6 => [0, 0, nil, "\343\203\214", nil, nil, nil],
-
0x32e7 => [0, 0, nil, "\343\203\215", nil, nil, nil],
-
0x32e8 => [0, 0, nil, "\343\203\216", nil, nil, nil],
-
0x32e9 => [0, 0, nil, "\343\203\217", nil, nil, nil],
-
0x32ea => [0, 0, nil, "\343\203\222", nil, nil, nil],
-
0x32eb => [0, 0, nil, "\343\203\225", nil, nil, nil],
-
0x32ec => [0, 0, nil, "\343\203\230", nil, nil, nil],
-
0x32ed => [0, 0, nil, "\343\203\233", nil, nil, nil],
-
0x32ee => [0, 0, nil, "\343\203\236", nil, nil, nil],
-
0x32ef => [0, 0, nil, "\343\203\237", nil, nil, nil],
-
0x32f0 => [0, 0, nil, "\343\203\240", nil, nil, nil],
-
0x32f1 => [0, 0, nil, "\343\203\241", nil, nil, nil],
-
0x32f2 => [0, 0, nil, "\343\203\242", nil, nil, nil],
-
0x32f3 => [0, 0, nil, "\343\203\244", nil, nil, nil],
-
0x32f4 => [0, 0, nil, "\343\203\246", nil, nil, nil],
-
0x32f5 => [0, 0, nil, "\343\203\250", nil, nil, nil],
-
0x32f6 => [0, 0, nil, "\343\203\251", nil, nil, nil],
-
0x32f7 => [0, 0, nil, "\343\203\252", nil, nil, nil],
-
0x32f8 => [0, 0, nil, "\343\203\253", nil, nil, nil],
-
0x32f9 => [0, 0, nil, "\343\203\254", nil, nil, nil],
-
0x32fa => [0, 0, nil, "\343\203\255", nil, nil, nil],
-
0x32fb => [0, 0, nil, "\343\203\257", nil, nil, nil],
-
0x32fc => [0, 0, nil, "\343\203\260", nil, nil, nil],
-
0x32fd => [0, 0, nil, "\343\203\261", nil, nil, nil],
-
0x32fe => [0, 0, nil, "\343\203\262", nil, nil, nil],
-
0x3300 => [0, 0, nil, "\343\202\242\343\203\221\343\203\274\343\203\210", nil, nil, nil],
-
0x3301 => [0, 0, nil, "\343\202\242\343\203\253\343\203\225\343\202\241", nil, nil, nil],
-
0x3302 => [0, 0, nil, "\343\202\242\343\203\263\343\203\232\343\202\242", nil, nil, nil],
-
0x3303 => [0, 0, nil, "\343\202\242\343\203\274\343\203\253", nil, nil, nil],
-
0x3304 => [0, 0, nil, "\343\202\244\343\203\213\343\203\263\343\202\260", nil, nil, nil],
-
0x3305 => [0, 0, nil, "\343\202\244\343\203\263\343\203\201", nil, nil, nil],
-
0x3306 => [0, 0, nil, "\343\202\246\343\202\251\343\203\263", nil, nil, nil],
-
0x3307 => [0, 0, nil, "\343\202\250\343\202\271\343\202\257\343\203\274\343\203\211", nil, nil, nil],
-
0x3308 => [0, 0, nil, "\343\202\250\343\203\274\343\202\253\343\203\274", nil, nil, nil],
-
0x3309 => [0, 0, nil, "\343\202\252\343\203\263\343\202\271", nil, nil, nil],
-
0x330a => [0, 0, nil, "\343\202\252\343\203\274\343\203\240", nil, nil, nil],
-
0x330b => [0, 0, nil, "\343\202\253\343\202\244\343\203\252", nil, nil, nil],
-
0x330c => [0, 0, nil, "\343\202\253\343\203\251\343\203\203\343\203\210", nil, nil, nil],
-
0x330d => [0, 0, nil, "\343\202\253\343\203\255\343\203\252\343\203\274", nil, nil, nil],
-
0x330e => [0, 0, nil, "\343\202\254\343\203\255\343\203\263", nil, nil, nil],
-
0x330f => [0, 0, nil, "\343\202\254\343\203\263\343\203\236", nil, nil, nil],
-
0x3310 => [0, 0, nil, "\343\202\256\343\202\254", nil, nil, nil],
-
0x3311 => [0, 0, nil, "\343\202\256\343\203\213\343\203\274", nil, nil, nil],
-
0x3312 => [0, 0, nil, "\343\202\255\343\203\245\343\203\252\343\203\274", nil, nil, nil],
-
0x3313 => [0, 0, nil, "\343\202\256\343\203\253\343\203\200\343\203\274", nil, nil, nil],
-
0x3314 => [0, 0, nil, "\343\202\255\343\203\255", nil, nil, nil],
-
0x3315 => [0, 0, nil, "\343\202\255\343\203\255\343\202\260\343\203\251\343\203\240", nil, nil, nil],
-
0x3316 => [0, 0, nil, "\343\202\255\343\203\255\343\203\241\343\203\274\343\203\210\343\203\253", nil, nil, nil],
-
0x3317 => [0, 0, nil, "\343\202\255\343\203\255\343\203\257\343\203\203\343\203\210", nil, nil, nil],
-
0x3318 => [0, 0, nil, "\343\202\260\343\203\251\343\203\240", nil, nil, nil],
-
0x3319 => [0, 0, nil, "\343\202\260\343\203\251\343\203\240\343\203\210\343\203\263", nil, nil, nil],
-
0x331a => [0, 0, nil, "\343\202\257\343\203\253\343\202\274\343\202\244\343\203\255", nil, nil, nil],
-
0x331b => [0, 0, nil, "\343\202\257\343\203\255\343\203\274\343\203\215", nil, nil, nil],
-
0x331c => [0, 0, nil, "\343\202\261\343\203\274\343\202\271", nil, nil, nil],
-
0x331d => [0, 0, nil, "\343\202\263\343\203\253\343\203\212", nil, nil, nil],
-
0x331e => [0, 0, nil, "\343\202\263\343\203\274\343\203\235", nil, nil, nil],
-
0x331f => [0, 0, nil, "\343\202\265\343\202\244\343\202\257\343\203\253", nil, nil, nil],
-
0x3320 => [0, 0, nil, "\343\202\265\343\203\263\343\203\201\343\203\274\343\203\240", nil, nil, nil],
-
0x3321 => [0, 0, nil, "\343\202\267\343\203\252\343\203\263\343\202\260", nil, nil, nil],
-
0x3322 => [0, 0, nil, "\343\202\273\343\203\263\343\203\201", nil, nil, nil],
-
0x3323 => [0, 0, nil, "\343\202\273\343\203\263\343\203\210", nil, nil, nil],
-
0x3324 => [0, 0, nil, "\343\203\200\343\203\274\343\202\271", nil, nil, nil],
-
0x3325 => [0, 0, nil, "\343\203\207\343\202\267", nil, nil, nil],
-
0x3326 => [0, 0, nil, "\343\203\211\343\203\253", nil, nil, nil],
-
0x3327 => [0, 0, nil, "\343\203\210\343\203\263", nil, nil, nil],
-
0x3328 => [0, 0, nil, "\343\203\212\343\203\216", nil, nil, nil],
-
0x3329 => [0, 0, nil, "\343\203\216\343\203\203\343\203\210", nil, nil, nil],
-
0x332a => [0, 0, nil, "\343\203\217\343\202\244\343\203\204", nil, nil, nil],
-
0x332b => [0, 0, nil, "\343\203\221\343\203\274\343\202\273\343\203\263\343\203\210", nil, nil, nil],
-
0x332c => [0, 0, nil, "\343\203\221\343\203\274\343\203\204", nil, nil, nil],
-
0x332d => [0, 0, nil, "\343\203\220\343\203\274\343\203\254\343\203\253", nil, nil, nil],
-
0x332e => [0, 0, nil, "\343\203\224\343\202\242\343\202\271\343\203\210\343\203\253", nil, nil, nil],
-
0x332f => [0, 0, nil, "\343\203\224\343\202\257\343\203\253", nil, nil, nil],
-
0x3330 => [0, 0, nil, "\343\203\224\343\202\263", nil, nil, nil],
-
0x3331 => [0, 0, nil, "\343\203\223\343\203\253", nil, nil, nil],
-
0x3332 => [0, 0, nil, "\343\203\225\343\202\241\343\203\251\343\203\203\343\203\211", nil, nil, nil],
-
0x3333 => [0, 0, nil, "\343\203\225\343\202\243\343\203\274\343\203\210", nil, nil, nil],
-
0x3334 => [0, 0, nil, "\343\203\226\343\203\203\343\202\267\343\202\247\343\203\253", nil, nil, nil],
-
0x3335 => [0, 0, nil, "\343\203\225\343\203\251\343\203\263", nil, nil, nil],
-
0x3336 => [0, 0, nil, "\343\203\230\343\202\257\343\202\277\343\203\274\343\203\253", nil, nil, nil],
-
0x3337 => [0, 0, nil, "\343\203\232\343\202\275", nil, nil, nil],
-
0x3338 => [0, 0, nil, "\343\203\232\343\203\213\343\203\222", nil, nil, nil],
-
0x3339 => [0, 0, nil, "\343\203\230\343\203\253\343\203\204", nil, nil, nil],
-
0x333a => [0, 0, nil, "\343\203\232\343\203\263\343\202\271", nil, nil, nil],
-
0x333b => [0, 0, nil, "\343\203\232\343\203\274\343\202\270", nil, nil, nil],
-
0x333c => [0, 0, nil, "\343\203\231\343\203\274\343\202\277", nil, nil, nil],
-
0x333d => [0, 0, nil, "\343\203\235\343\202\244\343\203\263\343\203\210", nil, nil, nil],
-
0x333e => [0, 0, nil, "\343\203\234\343\203\253\343\203\210", nil, nil, nil],
-
0x333f => [0, 0, nil, "\343\203\233\343\203\263", nil, nil, nil],
-
0x3340 => [0, 0, nil, "\343\203\235\343\203\263\343\203\211", nil, nil, nil],
-
0x3341 => [0, 0, nil, "\343\203\233\343\203\274\343\203\253", nil, nil, nil],
-
0x3342 => [0, 0, nil, "\343\203\233\343\203\274\343\203\263", nil, nil, nil],
-
0x3343 => [0, 0, nil, "\343\203\236\343\202\244\343\202\257\343\203\255", nil, nil, nil],
-
0x3344 => [0, 0, nil, "\343\203\236\343\202\244\343\203\253", nil, nil, nil],
-
0x3345 => [0, 0, nil, "\343\203\236\343\203\203\343\203\217", nil, nil, nil],
-
0x3346 => [0, 0, nil, "\343\203\236\343\203\253\343\202\257", nil, nil, nil],
-
0x3347 => [0, 0, nil, "\343\203\236\343\203\263\343\202\267\343\203\247\343\203\263", nil, nil, nil],
-
0x3348 => [0, 0, nil, "\343\203\237\343\202\257\343\203\255\343\203\263", nil, nil, nil],
-
0x3349 => [0, 0, nil, "\343\203\237\343\203\252", nil, nil, nil],
-
0x334a => [0, 0, nil, "\343\203\237\343\203\252\343\203\220\343\203\274\343\203\253", nil, nil, nil],
-
0x334b => [0, 0, nil, "\343\203\241\343\202\254", nil, nil, nil],
-
0x334c => [0, 0, nil, "\343\203\241\343\202\254\343\203\210\343\203\263", nil, nil, nil],
-
0x334d => [0, 0, nil, "\343\203\241\343\203\274\343\203\210\343\203\253", nil, nil, nil],
-
0x334e => [0, 0, nil, "\343\203\244\343\203\274\343\203\211", nil, nil, nil],
-
0x334f => [0, 0, nil, "\343\203\244\343\203\274\343\203\253", nil, nil, nil],
-
0x3350 => [0, 0, nil, "\343\203\246\343\202\242\343\203\263", nil, nil, nil],
-
0x3351 => [0, 0, nil, "\343\203\252\343\203\203\343\203\210\343\203\253", nil, nil, nil],
-
0x3352 => [0, 0, nil, "\343\203\252\343\203\251", nil, nil, nil],
-
0x3353 => [0, 0, nil, "\343\203\253\343\203\224\343\203\274", nil, nil, nil],
-
0x3354 => [0, 0, nil, "\343\203\253\343\203\274\343\203\226\343\203\253", nil, nil, nil],
-
0x3355 => [0, 0, nil, "\343\203\254\343\203\240", nil, nil, nil],
-
0x3356 => [0, 0, nil, "\343\203\254\343\203\263\343\203\210\343\202\262\343\203\263", nil, nil, nil],
-
0x3357 => [0, 0, nil, "\343\203\257\343\203\203\343\203\210", nil, nil, nil],
-
0x3358 => [0, 0, nil, "0\347\202\271", nil, nil, nil],
-
0x3359 => [0, 0, nil, "1\347\202\271", nil, nil, nil],
-
0x335a => [0, 0, nil, "2\347\202\271", nil, nil, nil],
-
0x335b => [0, 0, nil, "3\347\202\271", nil, nil, nil],
-
0x335c => [0, 0, nil, "4\347\202\271", nil, nil, nil],
-
0x335d => [0, 0, nil, "5\347\202\271", nil, nil, nil],
-
0x335e => [0, 0, nil, "6\347\202\271", nil, nil, nil],
-
0x335f => [0, 0, nil, "7\347\202\271", nil, nil, nil],
-
0x3360 => [0, 0, nil, "8\347\202\271", nil, nil, nil],
-
0x3361 => [0, 0, nil, "9\347\202\271", nil, nil, nil],
-
0x3362 => [0, 0, nil, "10\347\202\271", nil, nil, nil],
-
0x3363 => [0, 0, nil, "11\347\202\271", nil, nil, nil],
-
0x3364 => [0, 0, nil, "12\347\202\271", nil, nil, nil],
-
0x3365 => [0, 0, nil, "13\347\202\271", nil, nil, nil],
-
0x3366 => [0, 0, nil, "14\347\202\271", nil, nil, nil],
-
0x3367 => [0, 0, nil, "15\347\202\271", nil, nil, nil],
-
0x3368 => [0, 0, nil, "16\347\202\271", nil, nil, nil],
-
0x3369 => [0, 0, nil, "17\347\202\271", nil, nil, nil],
-
0x336a => [0, 0, nil, "18\347\202\271", nil, nil, nil],
-
0x336b => [0, 0, nil, "19\347\202\271", nil, nil, nil],
-
0x336c => [0, 0, nil, "20\347\202\271", nil, nil, nil],
-
0x336d => [0, 0, nil, "21\347\202\271", nil, nil, nil],
-
0x336e => [0, 0, nil, "22\347\202\271", nil, nil, nil],
-
0x336f => [0, 0, nil, "23\347\202\271", nil, nil, nil],
-
0x3370 => [0, 0, nil, "24\347\202\271", nil, nil, nil],
-
0x3371 => [0, 0, nil, "hPa", nil, nil, nil],
-
0x3372 => [0, 0, nil, "da", nil, nil, nil],
-
0x3373 => [0, 0, nil, "AU", nil, nil, nil],
-
0x3374 => [0, 0, nil, "bar", nil, nil, nil],
-
0x3375 => [0, 0, nil, "oV", nil, nil, nil],
-
0x3376 => [0, 0, nil, "pc", nil, nil, nil],
-
0x337b => [0, 0, nil, "\345\271\263\346\210\220", nil, nil, nil],
-
0x337c => [0, 0, nil, "\346\230\255\345\222\214", nil, nil, nil],
-
0x337d => [0, 0, nil, "\345\244\247\346\255\243", nil, nil, nil],
-
0x337e => [0, 0, nil, "\346\230\216\346\262\273", nil, nil, nil],
-
0x337f => [0, 0, nil, "\346\240\252\345\274\217\344\274\232\347\244\276", nil, nil, nil],
-
0x3380 => [0, 0, nil, "pA", nil, nil, nil],
-
0x3381 => [0, 0, nil, "nA", nil, nil, nil],
-
0x3382 => [0, 0, nil, "\316\274A", nil, nil, nil],
-
0x3383 => [0, 0, nil, "mA", nil, nil, nil],
-
0x3384 => [0, 0, nil, "kA", nil, nil, nil],
-
0x3385 => [0, 0, nil, "KB", nil, nil, nil],
-
0x3386 => [0, 0, nil, "MB", nil, nil, nil],
-
0x3387 => [0, 0, nil, "GB", nil, nil, nil],
-
0x3388 => [0, 0, nil, "cal", nil, nil, nil],
-
0x3389 => [0, 0, nil, "kcal", nil, nil, nil],
-
0x338a => [0, 0, nil, "pF", nil, nil, nil],
-
0x338b => [0, 0, nil, "nF", nil, nil, nil],
-
0x338c => [0, 0, nil, "\316\274F", nil, nil, nil],
-
0x338d => [0, 0, nil, "\316\274g", nil, nil, nil],
-
0x338e => [0, 0, nil, "mg", nil, nil, nil],
-
0x338f => [0, 0, nil, "kg", nil, nil, nil],
-
0x3390 => [0, 0, nil, "Hz", nil, nil, nil],
-
0x3391 => [0, 0, nil, "kHz", nil, nil, nil],
-
0x3392 => [0, 0, nil, "MHz", nil, nil, nil],
-
0x3393 => [0, 0, nil, "GHz", nil, nil, nil],
-
0x3394 => [0, 0, nil, "THz", nil, nil, nil],
-
0x3395 => [0, 0, nil, "\316\274\342\204\223", nil, nil, nil],
-
0x3396 => [0, 0, nil, "m\342\204\223", nil, nil, nil],
-
0x3397 => [0, 0, nil, "d\342\204\223", nil, nil, nil],
-
0x3398 => [0, 0, nil, "k\342\204\223", nil, nil, nil],
-
0x3399 => [0, 0, nil, "fm", nil, nil, nil],
-
0x339a => [0, 0, nil, "nm", nil, nil, nil],
-
0x339b => [0, 0, nil, "\316\274m", nil, nil, nil],
-
0x339c => [0, 0, nil, "mm", nil, nil, nil],
-
0x339d => [0, 0, nil, "cm", nil, nil, nil],
-
0x339e => [0, 0, nil, "km", nil, nil, nil],
-
0x339f => [0, 0, nil, "mm\302\262", nil, nil, nil],
-
0x33a0 => [0, 0, nil, "cm\302\262", nil, nil, nil],
-
0x33a1 => [0, 0, nil, "m\302\262", nil, nil, nil],
-
0x33a2 => [0, 0, nil, "km\302\262", nil, nil, nil],
-
0x33a3 => [0, 0, nil, "mm\302\263", nil, nil, nil],
-
0x33a4 => [0, 0, nil, "cm\302\263", nil, nil, nil],
-
0x33a5 => [0, 0, nil, "m\302\263", nil, nil, nil],
-
0x33a6 => [0, 0, nil, "km\302\263", nil, nil, nil],
-
0x33a7 => [0, 0, nil, "m\342\210\225s", nil, nil, nil],
-
0x33a8 => [0, 0, nil, "m\342\210\225s\302\262", nil, nil, nil],
-
0x33a9 => [0, 0, nil, "Pa", nil, nil, nil],
-
0x33aa => [0, 0, nil, "kPa", nil, nil, nil],
-
0x33ab => [0, 0, nil, "MPa", nil, nil, nil],
-
0x33ac => [0, 0, nil, "GPa", nil, nil, nil],
-
0x33ad => [0, 0, nil, "rad", nil, nil, nil],
-
0x33ae => [0, 0, nil, "rad\342\210\225s", nil, nil, nil],
-
0x33af => [0, 0, nil, "rad\342\210\225s\302\262", nil, nil, nil],
-
0x33b0 => [0, 0, nil, "ps", nil, nil, nil],
-
0x33b1 => [0, 0, nil, "ns", nil, nil, nil],
-
0x33b2 => [0, 0, nil, "\316\274s", nil, nil, nil],
-
0x33b3 => [0, 0, nil, "ms", nil, nil, nil],
-
0x33b4 => [0, 0, nil, "pV", nil, nil, nil],
-
0x33b5 => [0, 0, nil, "nV", nil, nil, nil],
-
0x33b6 => [0, 0, nil, "\316\274V", nil, nil, nil],
-
0x33b7 => [0, 0, nil, "mV", nil, nil, nil],
-
0x33b8 => [0, 0, nil, "kV", nil, nil, nil],
-
0x33b9 => [0, 0, nil, "MV", nil, nil, nil],
-
0x33ba => [0, 0, nil, "pW", nil, nil, nil],
-
0x33bb => [0, 0, nil, "nW", nil, nil, nil],
-
0x33bc => [0, 0, nil, "\316\274W", nil, nil, nil],
-
0x33bd => [0, 0, nil, "mW", nil, nil, nil],
-
0x33be => [0, 0, nil, "kW", nil, nil, nil],
-
0x33bf => [0, 0, nil, "MW", nil, nil, nil],
-
0x33c0 => [0, 0, nil, "k\316\251", nil, nil, nil],
-
0x33c1 => [0, 0, nil, "M\316\251", nil, nil, nil],
-
0x33c2 => [0, 0, nil, "a.m.", nil, nil, nil],
-
0x33c3 => [0, 0, nil, "Bq", nil, nil, nil],
-
0x33c4 => [0, 0, nil, "cc", nil, nil, nil],
-
0x33c5 => [0, 0, nil, "cd", nil, nil, nil],
-
0x33c6 => [0, 0, nil, "C\342\210\225kg", nil, nil, nil],
-
0x33c7 => [0, 0, nil, "Co.", nil, nil, nil],
-
0x33c8 => [0, 0, nil, "dB", nil, nil, nil],
-
0x33c9 => [0, 0, nil, "Gy", nil, nil, nil],
-
0x33ca => [0, 0, nil, "ha", nil, nil, nil],
-
0x33cb => [0, 0, nil, "HP", nil, nil, nil],
-
0x33cc => [0, 0, nil, "in", nil, nil, nil],
-
0x33cd => [0, 0, nil, "KK", nil, nil, nil],
-
0x33ce => [0, 0, nil, "KM", nil, nil, nil],
-
0x33cf => [0, 0, nil, "kt", nil, nil, nil],
-
0x33d0 => [0, 0, nil, "lm", nil, nil, nil],
-
0x33d1 => [0, 0, nil, "ln", nil, nil, nil],
-
0x33d2 => [0, 0, nil, "log", nil, nil, nil],
-
0x33d3 => [0, 0, nil, "lx", nil, nil, nil],
-
0x33d4 => [0, 0, nil, "mb", nil, nil, nil],
-
0x33d5 => [0, 0, nil, "mil", nil, nil, nil],
-
0x33d6 => [0, 0, nil, "mol", nil, nil, nil],
-
0x33d7 => [0, 0, nil, "PH", nil, nil, nil],
-
0x33d8 => [0, 0, nil, "p.m.", nil, nil, nil],
-
0x33d9 => [0, 0, nil, "PPM", nil, nil, nil],
-
0x33da => [0, 0, nil, "PR", nil, nil, nil],
-
0x33db => [0, 0, nil, "sr", nil, nil, nil],
-
0x33dc => [0, 0, nil, "Sv", nil, nil, nil],
-
0x33dd => [0, 0, nil, "Wb", nil, nil, nil],
-
0x33e0 => [0, 0, nil, "1\346\227\245", nil, nil, nil],
-
0x33e1 => [0, 0, nil, "2\346\227\245", nil, nil, nil],
-
0x33e2 => [0, 0, nil, "3\346\227\245", nil, nil, nil],
-
0x33e3 => [0, 0, nil, "4\346\227\245", nil, nil, nil],
-
0x33e4 => [0, 0, nil, "5\346\227\245", nil, nil, nil],
-
0x33e5 => [0, 0, nil, "6\346\227\245", nil, nil, nil],
-
0x33e6 => [0, 0, nil, "7\346\227\245", nil, nil, nil],
-
0x33e7 => [0, 0, nil, "8\346\227\245", nil, nil, nil],
-
0x33e8 => [0, 0, nil, "9\346\227\245", nil, nil, nil],
-
0x33e9 => [0, 0, nil, "10\346\227\245", nil, nil, nil],
-
0x33ea => [0, 0, nil, "11\346\227\245", nil, nil, nil],
-
0x33eb => [0, 0, nil, "12\346\227\245", nil, nil, nil],
-
0x33ec => [0, 0, nil, "13\346\227\245", nil, nil, nil],
-
0x33ed => [0, 0, nil, "14\346\227\245", nil, nil, nil],
-
0x33ee => [0, 0, nil, "15\346\227\245", nil, nil, nil],
-
0x33ef => [0, 0, nil, "16\346\227\245", nil, nil, nil],
-
0x33f0 => [0, 0, nil, "17\346\227\245", nil, nil, nil],
-
0x33f1 => [0, 0, nil, "18\346\227\245", nil, nil, nil],
-
0x33f2 => [0, 0, nil, "19\346\227\245", nil, nil, nil],
-
0x33f3 => [0, 0, nil, "20\346\227\245", nil, nil, nil],
-
0x33f4 => [0, 0, nil, "21\346\227\245", nil, nil, nil],
-
0x33f5 => [0, 0, nil, "22\346\227\245", nil, nil, nil],
-
0x33f6 => [0, 0, nil, "23\346\227\245", nil, nil, nil],
-
0x33f7 => [0, 0, nil, "24\346\227\245", nil, nil, nil],
-
0x33f8 => [0, 0, nil, "25\346\227\245", nil, nil, nil],
-
0x33f9 => [0, 0, nil, "26\346\227\245", nil, nil, nil],
-
0x33fa => [0, 0, nil, "27\346\227\245", nil, nil, nil],
-
0x33fb => [0, 0, nil, "28\346\227\245", nil, nil, nil],
-
0x33fc => [0, 0, nil, "29\346\227\245", nil, nil, nil],
-
0x33fd => [0, 0, nil, "30\346\227\245", nil, nil, nil],
-
0x33fe => [0, 0, nil, "31\346\227\245", nil, nil, nil],
-
0xf900 => [0, 2, "\350\261\210", "\350\261\210", nil, nil, nil],
-
0xf901 => [0, 2, "\346\233\264", "\346\233\264", nil, nil, nil],
-
0xf902 => [0, 2, "\350\273\212", "\350\273\212", nil, nil, nil],
-
0xf903 => [0, 2, "\350\263\210", "\350\263\210", nil, nil, nil],
-
0xf904 => [0, 2, "\346\273\221", "\346\273\221", nil, nil, nil],
-
0xf905 => [0, 2, "\344\270\262", "\344\270\262", nil, nil, nil],
-
0xf906 => [0, 2, "\345\217\245", "\345\217\245", nil, nil, nil],
-
0xf907 => [0, 2, "\351\276\234", "\351\276\234", nil, nil, nil],
-
0xf908 => [0, 2, "\351\276\234", "\351\276\234", nil, nil, nil],
-
0xf909 => [0, 2, "\345\245\221", "\345\245\221", nil, nil, nil],
-
0xf90a => [0, 2, "\351\207\221", "\351\207\221", nil, nil, nil],
-
0xf90b => [0, 2, "\345\226\207", "\345\226\207", nil, nil, nil],
-
0xf90c => [0, 2, "\345\245\210", "\345\245\210", nil, nil, nil],
-
0xf90d => [0, 2, "\346\207\266", "\346\207\266", nil, nil, nil],
-
0xf90e => [0, 2, "\347\231\251", "\347\231\251", nil, nil, nil],
-
0xf90f => [0, 2, "\347\276\205", "\347\276\205", nil, nil, nil],
-
0xf910 => [0, 2, "\350\230\277", "\350\230\277", nil, nil, nil],
-
0xf911 => [0, 2, "\350\236\272", "\350\236\272", nil, nil, nil],
-
0xf912 => [0, 2, "\350\243\270", "\350\243\270", nil, nil, nil],
-
0xf913 => [0, 2, "\351\202\217", "\351\202\217", nil, nil, nil],
-
0xf914 => [0, 2, "\346\250\202", "\346\250\202", nil, nil, nil],
-
0xf915 => [0, 2, "\346\264\233", "\346\264\233", nil, nil, nil],
-
0xf916 => [0, 2, "\347\203\231", "\347\203\231", nil, nil, nil],
-
0xf917 => [0, 2, "\347\217\236", "\347\217\236", nil, nil, nil],
-
0xf918 => [0, 2, "\350\220\275", "\350\220\275", nil, nil, nil],
-
0xf919 => [0, 2, "\351\205\252", "\351\205\252", nil, nil, nil],
-
0xf91a => [0, 2, "\351\247\261", "\351\247\261", nil, nil, nil],
-
0xf91b => [0, 2, "\344\272\202", "\344\272\202", nil, nil, nil],
-
0xf91c => [0, 2, "\345\215\265", "\345\215\265", nil, nil, nil],
-
0xf91d => [0, 2, "\346\254\204", "\346\254\204", nil, nil, nil],
-
0xf91e => [0, 2, "\347\210\233", "\347\210\233", nil, nil, nil],
-
0xf91f => [0, 2, "\350\230\255", "\350\230\255", nil, nil, nil],
-
0xf920 => [0, 2, "\351\270\236", "\351\270\236", nil, nil, nil],
-
0xf921 => [0, 2, "\345\265\220", "\345\265\220", nil, nil, nil],
-
0xf922 => [0, 2, "\346\277\253", "\346\277\253", nil, nil, nil],
-
0xf923 => [0, 2, "\350\227\215", "\350\227\215", nil, nil, nil],
-
0xf924 => [0, 2, "\350\245\244", "\350\245\244", nil, nil, nil],
-
0xf925 => [0, 2, "\346\213\211", "\346\213\211", nil, nil, nil],
-
0xf926 => [0, 2, "\350\207\230", "\350\207\230", nil, nil, nil],
-
0xf927 => [0, 2, "\350\240\237", "\350\240\237", nil, nil, nil],
-
0xf928 => [0, 2, "\345\273\212", "\345\273\212", nil, nil, nil],
-
0xf929 => [0, 2, "\346\234\227", "\346\234\227", nil, nil, nil],
-
0xf92a => [0, 2, "\346\265\252", "\346\265\252", nil, nil, nil],
-
0xf92b => [0, 2, "\347\213\274", "\347\213\274", nil, nil, nil],
-
0xf92c => [0, 2, "\351\203\216", "\351\203\216", nil, nil, nil],
-
0xf92d => [0, 2, "\344\276\206", "\344\276\206", nil, nil, nil],
-
0xf92e => [0, 2, "\345\206\267", "\345\206\267", nil, nil, nil],
-
0xf92f => [0, 2, "\345\213\236", "\345\213\236", nil, nil, nil],
-
0xf930 => [0, 2, "\346\223\204", "\346\223\204", nil, nil, nil],
-
0xf931 => [0, 2, "\346\253\223", "\346\253\223", nil, nil, nil],
-
0xf932 => [0, 2, "\347\210\220", "\347\210\220", nil, nil, nil],
-
0xf933 => [0, 2, "\347\233\247", "\347\233\247", nil, nil, nil],
-
0xf934 => [0, 2, "\350\200\201", "\350\200\201", nil, nil, nil],
-
0xf935 => [0, 2, "\350\230\206", "\350\230\206", nil, nil, nil],
-
0xf936 => [0, 2, "\350\231\234", "\350\231\234", nil, nil, nil],
-
0xf937 => [0, 2, "\350\267\257", "\350\267\257", nil, nil, nil],
-
0xf938 => [0, 2, "\351\234\262", "\351\234\262", nil, nil, nil],
-
0xf939 => [0, 2, "\351\255\257", "\351\255\257", nil, nil, nil],
-
0xf93a => [0, 2, "\351\267\272", "\351\267\272", nil, nil, nil],
-
0xf93b => [0, 2, "\347\242\214", "\347\242\214", nil, nil, nil],
-
0xf93c => [0, 2, "\347\245\277", "\347\245\277", nil, nil, nil],
-
0xf93d => [0, 2, "\347\266\240", "\347\266\240", nil, nil, nil],
-
0xf93e => [0, 2, "\350\217\211", "\350\217\211", nil, nil, nil],
-
0xf93f => [0, 2, "\351\214\204", "\351\214\204", nil, nil, nil],
-
0xf940 => [0, 2, "\351\271\277", "\351\271\277", nil, nil, nil],
-
0xf941 => [0, 2, "\350\253\226", "\350\253\226", nil, nil, nil],
-
0xf942 => [0, 2, "\345\243\237", "\345\243\237", nil, nil, nil],
-
0xf943 => [0, 2, "\345\274\204", "\345\274\204", nil, nil, nil],
-
0xf944 => [0, 2, "\347\261\240", "\347\261\240", nil, nil, nil],
-
0xf945 => [0, 2, "\350\201\276", "\350\201\276", nil, nil, nil],
-
0xf946 => [0, 2, "\347\211\242", "\347\211\242", nil, nil, nil],
-
0xf947 => [0, 2, "\347\243\212", "\347\243\212", nil, nil, nil],
-
0xf948 => [0, 2, "\350\263\202", "\350\263\202", nil, nil, nil],
-
0xf949 => [0, 2, "\351\233\267", "\351\233\267", nil, nil, nil],
-
0xf94a => [0, 2, "\345\243\230", "\345\243\230", nil, nil, nil],
-
0xf94b => [0, 2, "\345\261\242", "\345\261\242", nil, nil, nil],
-
0xf94c => [0, 2, "\346\250\223", "\346\250\223", nil, nil, nil],
-
0xf94d => [0, 2, "\346\267\232", "\346\267\232", nil, nil, nil],
-
0xf94e => [0, 2, "\346\274\217", "\346\274\217", nil, nil, nil],
-
0xf94f => [0, 2, "\347\264\257", "\347\264\257", nil, nil, nil],
-
0xf950 => [0, 2, "\347\270\267", "\347\270\267", nil, nil, nil],
-
0xf951 => [0, 2, "\351\233\273", "\351\233\273", nil, nil, nil],
-
0xf952 => [0, 2, "\345\213\222", "\345\213\222", nil, nil, nil],
-
0xf953 => [0, 2, "\350\202\213", "\350\202\213", nil, nil, nil],
-
0xf954 => [0, 2, "\345\207\234", "\345\207\234", nil, nil, nil],
-
0xf955 => [0, 2, "\345\207\214", "\345\207\214", nil, nil, nil],
-
0xf956 => [0, 2, "\347\250\234", "\347\250\234", nil, nil, nil],
-
0xf957 => [0, 2, "\347\266\276", "\347\266\276", nil, nil, nil],
-
0xf958 => [0, 2, "\350\217\261", "\350\217\261", nil, nil, nil],
-
0xf959 => [0, 2, "\351\231\265", "\351\231\265", nil, nil, nil],
-
0xf95a => [0, 2, "\350\256\200", "\350\256\200", nil, nil, nil],
-
0xf95b => [0, 2, "\346\213\217", "\346\213\217", nil, nil, nil],
-
0xf95c => [0, 2, "\346\250\202", "\346\250\202", nil, nil, nil],
-
0xf95d => [0, 2, "\350\253\276", "\350\253\276", nil, nil, nil],
-
0xf95e => [0, 2, "\344\270\271", "\344\270\271", nil, nil, nil],
-
0xf95f => [0, 2, "\345\257\247", "\345\257\247", nil, nil, nil],
-
0xf960 => [0, 2, "\346\200\222", "\346\200\222", nil, nil, nil],
-
0xf961 => [0, 2, "\347\216\207", "\347\216\207", nil, nil, nil],
-
0xf962 => [0, 2, "\347\225\260", "\347\225\260", nil, nil, nil],
-
0xf963 => [0, 2, "\345\214\227", "\345\214\227", nil, nil, nil],
-
0xf964 => [0, 2, "\347\243\273", "\347\243\273", nil, nil, nil],
-
0xf965 => [0, 2, "\344\276\277", "\344\276\277", nil, nil, nil],
-
0xf966 => [0, 2, "\345\276\251", "\345\276\251", nil, nil, nil],
-
0xf967 => [0, 2, "\344\270\215", "\344\270\215", nil, nil, nil],
-
0xf968 => [0, 2, "\346\263\214", "\346\263\214", nil, nil, nil],
-
0xf969 => [0, 2, "\346\225\270", "\346\225\270", nil, nil, nil],
-
0xf96a => [0, 2, "\347\264\242", "\347\264\242", nil, nil, nil],
-
0xf96b => [0, 2, "\345\217\203", "\345\217\203", nil, nil, nil],
-
0xf96c => [0, 2, "\345\241\236", "\345\241\236", nil, nil, nil],
-
0xf96d => [0, 2, "\347\234\201", "\347\234\201", nil, nil, nil],
-
0xf96e => [0, 2, "\350\221\211", "\350\221\211", nil, nil, nil],
-
0xf96f => [0, 2, "\350\252\252", "\350\252\252", nil, nil, nil],
-
0xf970 => [0, 2, "\346\256\272", "\346\256\272", nil, nil, nil],
-
0xf971 => [0, 2, "\350\276\260", "\350\276\260", nil, nil, nil],
-
0xf972 => [0, 2, "\346\262\210", "\346\262\210", nil, nil, nil],
-
0xf973 => [0, 2, "\346\213\276", "\346\213\276", nil, nil, nil],
-
0xf974 => [0, 2, "\350\213\245", "\350\213\245", nil, nil, nil],
-
0xf975 => [0, 2, "\346\216\240", "\346\216\240", nil, nil, nil],
-
0xf976 => [0, 2, "\347\225\245", "\347\225\245", nil, nil, nil],
-
0xf977 => [0, 2, "\344\272\256", "\344\272\256", nil, nil, nil],
-
0xf978 => [0, 2, "\345\205\251", "\345\205\251", nil, nil, nil],
-
0xf979 => [0, 2, "\345\207\211", "\345\207\211", nil, nil, nil],
-
0xf97a => [0, 2, "\346\242\201", "\346\242\201", nil, nil, nil],
-
0xf97b => [0, 2, "\347\263\247", "\347\263\247", nil, nil, nil],
-
0xf97c => [0, 2, "\350\211\257", "\350\211\257", nil, nil, nil],
-
0xf97d => [0, 2, "\350\253\222", "\350\253\222", nil, nil, nil],
-
0xf97e => [0, 2, "\351\207\217", "\351\207\217", nil, nil, nil],
-
0xf97f => [0, 2, "\345\213\265", "\345\213\265", nil, nil, nil],
-
0xf980 => [0, 2, "\345\221\202", "\345\221\202", nil, nil, nil],
-
0xf981 => [0, 2, "\345\245\263", "\345\245\263", nil, nil, nil],
-
0xf982 => [0, 2, "\345\273\254", "\345\273\254", nil, nil, nil],
-
0xf983 => [0, 2, "\346\227\205", "\346\227\205", nil, nil, nil],
-
0xf984 => [0, 2, "\346\277\276", "\346\277\276", nil, nil, nil],
-
0xf985 => [0, 2, "\347\244\252", "\347\244\252", nil, nil, nil],
-
0xf986 => [0, 2, "\351\226\255", "\351\226\255", nil, nil, nil],
-
0xf987 => [0, 2, "\351\251\252", "\351\251\252", nil, nil, nil],
-
0xf988 => [0, 2, "\351\272\227", "\351\272\227", nil, nil, nil],
-
0xf989 => [0, 2, "\351\273\216", "\351\273\216", nil, nil, nil],
-
0xf98a => [0, 2, "\345\212\233", "\345\212\233", nil, nil, nil],
-
0xf98b => [0, 2, "\346\233\206", "\346\233\206", nil, nil, nil],
-
0xf98c => [0, 2, "\346\255\267", "\346\255\267", nil, nil, nil],
-
0xf98d => [0, 2, "\350\275\242", "\350\275\242", nil, nil, nil],
-
0xf98e => [0, 2, "\345\271\264", "\345\271\264", nil, nil, nil],
-
0xf98f => [0, 2, "\346\206\220", "\346\206\220", nil, nil, nil],
-
0xf990 => [0, 2, "\346\210\200", "\346\210\200", nil, nil, nil],
-
0xf991 => [0, 2, "\346\222\232", "\346\222\232", nil, nil, nil],
-
0xf992 => [0, 2, "\346\274\243", "\346\274\243", nil, nil, nil],
-
0xf993 => [0, 2, "\347\205\211", "\347\205\211", nil, nil, nil],
-
0xf994 => [0, 2, "\347\222\211", "\347\222\211", nil, nil, nil],
-
0xf995 => [0, 2, "\347\247\212", "\347\247\212", nil, nil, nil],
-
0xf996 => [0, 2, "\347\267\264", "\347\267\264", nil, nil, nil],
-
0xf997 => [0, 2, "\350\201\257", "\350\201\257", nil, nil, nil],
-
0xf998 => [0, 2, "\350\274\246", "\350\274\246", nil, nil, nil],
-
0xf999 => [0, 2, "\350\223\256", "\350\223\256", nil, nil, nil],
-
0xf99a => [0, 2, "\351\200\243", "\351\200\243", nil, nil, nil],
-
0xf99b => [0, 2, "\351\215\212", "\351\215\212", nil, nil, nil],
-
0xf99c => [0, 2, "\345\210\227", "\345\210\227", nil, nil, nil],
-
0xf99d => [0, 2, "\345\212\243", "\345\212\243", nil, nil, nil],
-
0xf99e => [0, 2, "\345\222\275", "\345\222\275", nil, nil, nil],
-
0xf99f => [0, 2, "\347\203\210", "\347\203\210", nil, nil, nil],
-
0xf9a0 => [0, 2, "\350\243\202", "\350\243\202", nil, nil, nil],
-
0xf9a1 => [0, 2, "\350\252\252", "\350\252\252", nil, nil, nil],
-
0xf9a2 => [0, 2, "\345\273\211", "\345\273\211", nil, nil, nil],
-
0xf9a3 => [0, 2, "\345\277\265", "\345\277\265", nil, nil, nil],
-
0xf9a4 => [0, 2, "\346\215\273", "\346\215\273", nil, nil, nil],
-
0xf9a5 => [0, 2, "\346\256\256", "\346\256\256", nil, nil, nil],
-
0xf9a6 => [0, 2, "\347\260\276", "\347\260\276", nil, nil, nil],
-
0xf9a7 => [0, 2, "\347\215\265", "\347\215\265", nil, nil, nil],
-
0xf9a8 => [0, 2, "\344\273\244", "\344\273\244", nil, nil, nil],
-
0xf9a9 => [0, 2, "\345\233\271", "\345\233\271", nil, nil, nil],
-
0xf9aa => [0, 2, "\345\257\247", "\345\257\247", nil, nil, nil],
-
0xf9ab => [0, 2, "\345\266\272", "\345\266\272", nil, nil, nil],
-
0xf9ac => [0, 2, "\346\200\234", "\346\200\234", nil, nil, nil],
-
0xf9ad => [0, 2, "\347\216\262", "\347\216\262", nil, nil, nil],
-
0xf9ae => [0, 2, "\347\221\251", "\347\221\251", nil, nil, nil],
-
0xf9af => [0, 2, "\347\276\232", "\347\276\232", nil, nil, nil],
-
0xf9b0 => [0, 2, "\350\201\206", "\350\201\206", nil, nil, nil],
-
0xf9b1 => [0, 2, "\351\210\264", "\351\210\264", nil, nil, nil],
-
0xf9b2 => [0, 2, "\351\233\266", "\351\233\266", nil, nil, nil],
-
0xf9b3 => [0, 2, "\351\235\210", "\351\235\210", nil, nil, nil],
-
0xf9b4 => [0, 2, "\351\240\230", "\351\240\230", nil, nil, nil],
-
0xf9b5 => [0, 2, "\344\276\213", "\344\276\213", nil, nil, nil],
-
0xf9b6 => [0, 2, "\347\246\256", "\347\246\256", nil, nil, nil],
-
0xf9b7 => [0, 2, "\351\206\264", "\351\206\264", nil, nil, nil],
-
0xf9b8 => [0, 2, "\351\232\270", "\351\232\270", nil, nil, nil],
-
0xf9b9 => [0, 2, "\346\203\241", "\346\203\241", nil, nil, nil],
-
0xf9ba => [0, 2, "\344\272\206", "\344\272\206", nil, nil, nil],
-
0xf9bb => [0, 2, "\345\203\232", "\345\203\232", nil, nil, nil],
-
0xf9bc => [0, 2, "\345\257\256", "\345\257\256", nil, nil, nil],
-
0xf9bd => [0, 2, "\345\260\277", "\345\260\277", nil, nil, nil],
-
0xf9be => [0, 2, "\346\226\231", "\346\226\231", nil, nil, nil],
-
0xf9bf => [0, 2, "\346\250\202", "\346\250\202", nil, nil, nil],
-
0xf9c0 => [0, 2, "\347\207\216", "\347\207\216", nil, nil, nil],
-
0xf9c1 => [0, 2, "\347\231\202", "\347\231\202", nil, nil, nil],
-
0xf9c2 => [0, 2, "\350\223\274", "\350\223\274", nil, nil, nil],
-
0xf9c3 => [0, 2, "\351\201\274", "\351\201\274", nil, nil, nil],
-
0xf9c4 => [0, 2, "\351\276\215", "\351\276\215", nil, nil, nil],
-
0xf9c5 => [0, 2, "\346\232\210", "\346\232\210", nil, nil, nil],
-
0xf9c6 => [0, 2, "\351\230\256", "\351\230\256", nil, nil, nil],
-
0xf9c7 => [0, 2, "\345\212\211", "\345\212\211", nil, nil, nil],
-
0xf9c8 => [0, 2, "\346\235\273", "\346\235\273", nil, nil, nil],
-
0xf9c9 => [0, 2, "\346\237\263", "\346\237\263", nil, nil, nil],
-
0xf9ca => [0, 2, "\346\265\201", "\346\265\201", nil, nil, nil],
-
0xf9cb => [0, 2, "\346\272\234", "\346\272\234", nil, nil, nil],
-
0xf9cc => [0, 2, "\347\220\211", "\347\220\211", nil, nil, nil],
-
0xf9cd => [0, 2, "\347\225\231", "\347\225\231", nil, nil, nil],
-
0xf9ce => [0, 2, "\347\241\253", "\347\241\253", nil, nil, nil],
-
0xf9cf => [0, 2, "\347\264\220", "\347\264\220", nil, nil, nil],
-
0xf9d0 => [0, 2, "\351\241\236", "\351\241\236", nil, nil, nil],
-
0xf9d1 => [0, 2, "\345\205\255", "\345\205\255", nil, nil, nil],
-
0xf9d2 => [0, 2, "\346\210\256", "\346\210\256", nil, nil, nil],
-
0xf9d3 => [0, 2, "\351\231\270", "\351\231\270", nil, nil, nil],
-
0xf9d4 => [0, 2, "\345\200\253", "\345\200\253", nil, nil, nil],
-
0xf9d5 => [0, 2, "\345\264\231", "\345\264\231", nil, nil, nil],
-
0xf9d6 => [0, 2, "\346\267\252", "\346\267\252", nil, nil, nil],
-
0xf9d7 => [0, 2, "\350\274\252", "\350\274\252", nil, nil, nil],
-
0xf9d8 => [0, 2, "\345\276\213", "\345\276\213", nil, nil, nil],
-
0xf9d9 => [0, 2, "\346\205\204", "\346\205\204", nil, nil, nil],
-
0xf9da => [0, 2, "\346\240\227", "\346\240\227", nil, nil, nil],
-
0xf9db => [0, 2, "\347\216\207", "\347\216\207", nil, nil, nil],
-
0xf9dc => [0, 2, "\351\232\206", "\351\232\206", nil, nil, nil],
-
0xf9dd => [0, 2, "\345\210\251", "\345\210\251", nil, nil, nil],
-
0xf9de => [0, 2, "\345\220\217", "\345\220\217", nil, nil, nil],
-
0xf9df => [0, 2, "\345\261\245", "\345\261\245", nil, nil, nil],
-
0xf9e0 => [0, 2, "\346\230\223", "\346\230\223", nil, nil, nil],
-
0xf9e1 => [0, 2, "\346\235\216", "\346\235\216", nil, nil, nil],
-
0xf9e2 => [0, 2, "\346\242\250", "\346\242\250", nil, nil, nil],
-
0xf9e3 => [0, 2, "\346\263\245", "\346\263\245", nil, nil, nil],
-
0xf9e4 => [0, 2, "\347\220\206", "\347\220\206", nil, nil, nil],
-
0xf9e5 => [0, 2, "\347\227\242", "\347\227\242", nil, nil, nil],
-
0xf9e6 => [0, 2, "\347\275\271", "\347\275\271", nil, nil, nil],
-
0xf9e7 => [0, 2, "\350\243\217", "\350\243\217", nil, nil, nil],
-
0xf9e8 => [0, 2, "\350\243\241", "\350\243\241", nil, nil, nil],
-
0xf9e9 => [0, 2, "\351\207\214", "\351\207\214", nil, nil, nil],
-
0xf9ea => [0, 2, "\351\233\242", "\351\233\242", nil, nil, nil],
-
0xf9eb => [0, 2, "\345\214\277", "\345\214\277", nil, nil, nil],
-
0xf9ec => [0, 2, "\346\272\272", "\346\272\272", nil, nil, nil],
-
0xf9ed => [0, 2, "\345\220\235", "\345\220\235", nil, nil, nil],
-
0xf9ee => [0, 2, "\347\207\220", "\347\207\220", nil, nil, nil],
-
0xf9ef => [0, 2, "\347\222\230", "\347\222\230", nil, nil, nil],
-
0xf9f0 => [0, 2, "\350\227\272", "\350\227\272", nil, nil, nil],
-
0xf9f1 => [0, 2, "\351\232\243", "\351\232\243", nil, nil, nil],
-
0xf9f2 => [0, 2, "\351\261\227", "\351\261\227", nil, nil, nil],
-
0xf9f3 => [0, 2, "\351\272\237", "\351\272\237", nil, nil, nil],
-
0xf9f4 => [0, 2, "\346\236\227", "\346\236\227", nil, nil, nil],
-
0xf9f5 => [0, 2, "\346\267\213", "\346\267\213", nil, nil, nil],
-
0xf9f6 => [0, 2, "\350\207\250", "\350\207\250", nil, nil, nil],
-
0xf9f7 => [0, 2, "\347\253\213", "\347\253\213", nil, nil, nil],
-
0xf9f8 => [0, 2, "\347\254\240", "\347\254\240", nil, nil, nil],
-
0xf9f9 => [0, 2, "\347\262\222", "\347\262\222", nil, nil, nil],
-
0xf9fa => [0, 2, "\347\213\200", "\347\213\200", nil, nil, nil],
-
0xf9fb => [0, 2, "\347\202\231", "\347\202\231", nil, nil, nil],
-
0xf9fc => [0, 2, "\350\255\230", "\350\255\230", nil, nil, nil],
-
0xf9fd => [0, 2, "\344\273\200", "\344\273\200", nil, nil, nil],
-
0xf9fe => [0, 2, "\350\214\266", "\350\214\266", nil, nil, nil],
-
0xf9ff => [0, 2, "\345\210\272", "\345\210\272", nil, nil, nil],
-
0xfa00 => [0, 2, "\345\210\207", "\345\210\207", nil, nil, nil],
-
0xfa01 => [0, 2, "\345\272\246", "\345\272\246", nil, nil, nil],
-
0xfa02 => [0, 2, "\346\213\223", "\346\213\223", nil, nil, nil],
-
0xfa03 => [0, 2, "\347\263\226", "\347\263\226", nil, nil, nil],
-
0xfa04 => [0, 2, "\345\256\205", "\345\256\205", nil, nil, nil],
-
0xfa05 => [0, 2, "\346\264\236", "\346\264\236", nil, nil, nil],
-
0xfa06 => [0, 2, "\346\232\264", "\346\232\264", nil, nil, nil],
-
0xfa07 => [0, 2, "\350\274\273", "\350\274\273", nil, nil, nil],
-
0xfa08 => [0, 2, "\350\241\214", "\350\241\214", nil, nil, nil],
-
0xfa09 => [0, 2, "\351\231\215", "\351\231\215", nil, nil, nil],
-
0xfa0a => [0, 2, "\350\246\213", "\350\246\213", nil, nil, nil],
-
0xfa0b => [0, 2, "\345\273\223", "\345\273\223", nil, nil, nil],
-
0xfa0c => [0, 2, "\345\205\200", "\345\205\200", nil, nil, nil],
-
0xfa0d => [0, 2, "\345\227\200", "\345\227\200", nil, nil, nil],
-
0xfa10 => [0, 2, "\345\241\232", "\345\241\232", nil, nil, nil],
-
0xfa12 => [0, 2, "\346\231\264", "\346\231\264", nil, nil, nil],
-
0xfa15 => [0, 2, "\345\207\236", "\345\207\236", nil, nil, nil],
-
0xfa16 => [0, 2, "\347\214\252", "\347\214\252", nil, nil, nil],
-
0xfa17 => [0, 2, "\347\233\212", "\347\233\212", nil, nil, nil],
-
0xfa18 => [0, 2, "\347\244\274", "\347\244\274", nil, nil, nil],
-
0xfa19 => [0, 2, "\347\245\236", "\347\245\236", nil, nil, nil],
-
0xfa1a => [0, 2, "\347\245\245", "\347\245\245", nil, nil, nil],
-
0xfa1b => [0, 2, "\347\246\217", "\347\246\217", nil, nil, nil],
-
0xfa1c => [0, 2, "\351\235\226", "\351\235\226", nil, nil, nil],
-
0xfa1d => [0, 2, "\347\262\276", "\347\262\276", nil, nil, nil],
-
0xfa1e => [0, 2, "\347\276\275", "\347\276\275", nil, nil, nil],
-
0xfa20 => [0, 2, "\350\230\222", "\350\230\222", nil, nil, nil],
-
0xfa22 => [0, 2, "\350\253\270", "\350\253\270", nil, nil, nil],
-
0xfa25 => [0, 2, "\351\200\270", "\351\200\270", nil, nil, nil],
-
0xfa26 => [0, 2, "\351\203\275", "\351\203\275", nil, nil, nil],
-
0xfa2a => [0, 2, "\351\243\257", "\351\243\257", nil, nil, nil],
-
0xfa2b => [0, 2, "\351\243\274", "\351\243\274", nil, nil, nil],
-
0xfa2c => [0, 2, "\351\244\250", "\351\244\250", nil, nil, nil],
-
0xfa2d => [0, 2, "\351\266\264", "\351\266\264", nil, nil, nil],
-
0xfb00 => [0, 0, nil, "ff", nil, nil, nil],
-
0xfb01 => [0, 0, nil, "fi", nil, nil, nil],
-
0xfb02 => [0, 0, nil, "fl", nil, nil, nil],
-
0xfb03 => [0, 0, nil, "ffi", nil, nil, nil],
-
0xfb04 => [0, 0, nil, "ffl", nil, nil, nil],
-
0xfb05 => [0, 0, nil, "\305\277t", nil, nil, nil],
-
0xfb06 => [0, 0, nil, "st", nil, nil, nil],
-
0xfb13 => [0, 0, nil, "\325\264\325\266", nil, nil, nil],
-
0xfb14 => [0, 0, nil, "\325\264\325\245", nil, nil, nil],
-
0xfb15 => [0, 0, nil, "\325\264\325\253", nil, nil, nil],
-
0xfb16 => [0, 0, nil, "\325\276\325\266", nil, nil, nil],
-
0xfb17 => [0, 0, nil, "\325\264\325\255", nil, nil, nil],
-
0xfb1d => [0, 0, "\327\231\326\264", "\327\231\326\264", nil, nil, nil],
-
0xfb1e => [26, 0, nil, nil, nil, nil, nil],
-
0xfb1f => [0, 1, "\327\262\326\267", "\327\262\326\267", nil, nil, nil],
-
0xfb20 => [0, 0, nil, "\327\242", nil, nil, nil],
-
0xfb21 => [0, 0, nil, "\327\220", nil, nil, nil],
-
0xfb22 => [0, 0, nil, "\327\223", nil, nil, nil],
-
0xfb23 => [0, 0, nil, "\327\224", nil, nil, nil],
-
0xfb24 => [0, 0, nil, "\327\233", nil, nil, nil],
-
0xfb25 => [0, 0, nil, "\327\234", nil, nil, nil],
-
0xfb26 => [0, 0, nil, "\327\235", nil, nil, nil],
-
0xfb27 => [0, 0, nil, "\327\250", nil, nil, nil],
-
0xfb28 => [0, 0, nil, "\327\252", nil, nil, nil],
-
0xfb29 => [0, 0, nil, "+", nil, nil, nil],
-
0xfb2a => [0, 1, "\327\251\327\201", "\327\251\327\201", nil, nil, nil],
-
0xfb2b => [0, 1, "\327\251\327\202", "\327\251\327\202", nil, nil, nil],
-
0xfb2c => [0, 1, "\357\255\211\327\201", "\357\255\211\327\201", nil, nil, nil],
-
0xfb2d => [0, 1, "\357\255\211\327\202", "\357\255\211\327\202", nil, nil, nil],
-
0xfb2e => [0, 1, "\327\220\326\267", "\327\220\326\267", nil, nil, nil],
-
0xfb2f => [0, 1, "\327\220\326\270", "\327\220\326\270", nil, nil, nil],
-
0xfb30 => [0, 1, "\327\220\326\274", "\327\220\326\274", nil, nil, nil],
-
0xfb31 => [0, 1, "\327\221\326\274", "\327\221\326\274", nil, nil, nil],
-
0xfb32 => [0, 1, "\327\222\326\274", "\327\222\326\274", nil, nil, nil],
-
0xfb33 => [0, 1, "\327\223\326\274", "\327\223\326\274", nil, nil, nil],
-
0xfb34 => [0, 1, "\327\224\326\274", "\327\224\326\274", nil, nil, nil],
-
0xfb35 => [0, 1, "\327\225\326\274", "\327\225\326\274", nil, nil, nil],
-
0xfb36 => [0, 1, "\327\226\326\274", "\327\226\326\274", nil, nil, nil],
-
0xfb38 => [0, 1, "\327\230\326\274", "\327\230\326\274", nil, nil, nil],
-
0xfb39 => [0, 1, "\327\231\326\274", "\327\231\326\274", nil, nil, nil],
-
0xfb3a => [0, 1, "\327\232\326\274", "\327\232\326\274", nil, nil, nil],
-
0xfb3b => [0, 1, "\327\233\326\274", "\327\233\326\274", nil, nil, nil],
-
0xfb3c => [0, 1, "\327\234\326\274", "\327\234\326\274", nil, nil, nil],
-
0xfb3e => [0, 1, "\327\236\326\274", "\327\236\326\274", nil, nil, nil],
-
0xfb40 => [0, 1, "\327\240\326\274", "\327\240\326\274", nil, nil, nil],
-
0xfb41 => [0, 1, "\327\241\326\274", "\327\241\326\274", nil, nil, nil],
-
0xfb43 => [0, 1, "\327\243\326\274", "\327\243\326\274", nil, nil, nil],
-
0xfb44 => [0, 1, "\327\244\326\274", "\327\244\326\274", nil, nil, nil],
-
0xfb46 => [0, 1, "\327\246\326\274", "\327\246\326\274", nil, nil, nil],
-
0xfb47 => [0, 1, "\327\247\326\274", "\327\247\326\274", nil, nil, nil],
-
0xfb48 => [0, 1, "\327\250\326\274", "\327\250\326\274", nil, nil, nil],
-
0xfb49 => [0, 1, "\327\251\326\274", "\327\251\326\274", nil, nil, nil],
-
0xfb4a => [0, 1, "\327\252\326\274", "\327\252\326\274", nil, nil, nil],
-
0xfb4b => [0, 1, "\327\225\326\271", "\327\225\326\271", nil, nil, nil],
-
0xfb4c => [0, 1, "\327\221\326\277", "\327\221\326\277", nil, nil, nil],
-
0xfb4d => [0, 1, "\327\233\326\277", "\327\233\326\277", nil, nil, nil],
-
0xfb4e => [0, 1, "\327\244\326\277", "\327\244\326\277", nil, nil, nil],
-
0xfb4f => [0, 0, nil, "\327\220\327\234", nil, nil, nil],
-
0xfb50 => [0, 0, nil, "\331\261", nil, nil, nil],
-
0xfb51 => [0, 0, nil, "\331\261", nil, nil, nil],
-
0xfb52 => [0, 0, nil, "\331\273", nil, nil, nil],
-
0xfb53 => [0, 0, nil, "\331\273", nil, nil, nil],
-
0xfb54 => [0, 0, nil, "\331\273", nil, nil, nil],
-
0xfb55 => [0, 0, nil, "\331\273", nil, nil, nil],
-
0xfb56 => [0, 0, nil, "\331\276", nil, nil, nil],
-
0xfb57 => [0, 0, nil, "\331\276", nil, nil, nil],
-
0xfb58 => [0, 0, nil, "\331\276", nil, nil, nil],
-
0xfb59 => [0, 0, nil, "\331\276", nil, nil, nil],
-
0xfb5a => [0, 0, nil, "\332\200", nil, nil, nil],
-
0xfb5b => [0, 0, nil, "\332\200", nil, nil, nil],
-
0xfb5c => [0, 0, nil, "\332\200", nil, nil, nil],
-
0xfb5d => [0, 0, nil, "\332\200", nil, nil, nil],
-
0xfb5e => [0, 0, nil, "\331\272", nil, nil, nil],
-
0xfb5f => [0, 0, nil, "\331\272", nil, nil, nil],
-
0xfb60 => [0, 0, nil, "\331\272", nil, nil, nil],
-
0xfb61 => [0, 0, nil, "\331\272", nil, nil, nil],
-
0xfb62 => [0, 0, nil, "\331\277", nil, nil, nil],
-
0xfb63 => [0, 0, nil, "\331\277", nil, nil, nil],
-
0xfb64 => [0, 0, nil, "\331\277", nil, nil, nil],
-
0xfb65 => [0, 0, nil, "\331\277", nil, nil, nil],
-
0xfb66 => [0, 0, nil, "\331\271", nil, nil, nil],
-
0xfb67 => [0, 0, nil, "\331\271", nil, nil, nil],
-
0xfb68 => [0, 0, nil, "\331\271", nil, nil, nil],
-
0xfb69 => [0, 0, nil, "\331\271", nil, nil, nil],
-
0xfb6a => [0, 0, nil, "\332\244", nil, nil, nil],
-
0xfb6b => [0, 0, nil, "\332\244", nil, nil, nil],
-
0xfb6c => [0, 0, nil, "\332\244", nil, nil, nil],
-
0xfb6d => [0, 0, nil, "\332\244", nil, nil, nil],
-
0xfb6e => [0, 0, nil, "\332\246", nil, nil, nil],
-
0xfb6f => [0, 0, nil, "\332\246", nil, nil, nil],
-
0xfb70 => [0, 0, nil, "\332\246", nil, nil, nil],
-
0xfb71 => [0, 0, nil, "\332\246", nil, nil, nil],
-
0xfb72 => [0, 0, nil, "\332\204", nil, nil, nil],
-
0xfb73 => [0, 0, nil, "\332\204", nil, nil, nil],
-
0xfb74 => [0, 0, nil, "\332\204", nil, nil, nil],
-
0xfb75 => [0, 0, nil, "\332\204", nil, nil, nil],
-
0xfb76 => [0, 0, nil, "\332\203", nil, nil, nil],
-
0xfb77 => [0, 0, nil, "\332\203", nil, nil, nil],
-
0xfb78 => [0, 0, nil, "\332\203", nil, nil, nil],
-
0xfb79 => [0, 0, nil, "\332\203", nil, nil, nil],
-
0xfb7a => [0, 0, nil, "\332\206", nil, nil, nil],
-
0xfb7b => [0, 0, nil, "\332\206", nil, nil, nil],
-
0xfb7c => [0, 0, nil, "\332\206", nil, nil, nil],
-
0xfb7d => [0, 0, nil, "\332\206", nil, nil, nil],
-
0xfb7e => [0, 0, nil, "\332\207", nil, nil, nil],
-
0xfb7f => [0, 0, nil, "\332\207", nil, nil, nil],
-
0xfb80 => [0, 0, nil, "\332\207", nil, nil, nil],
-
0xfb81 => [0, 0, nil, "\332\207", nil, nil, nil],
-
0xfb82 => [0, 0, nil, "\332\215", nil, nil, nil],
-
0xfb83 => [0, 0, nil, "\332\215", nil, nil, nil],
-
0xfb84 => [0, 0, nil, "\332\214", nil, nil, nil],
-
0xfb85 => [0, 0, nil, "\332\214", nil, nil, nil],
-
0xfb86 => [0, 0, nil, "\332\216", nil, nil, nil],
-
0xfb87 => [0, 0, nil, "\332\216", nil, nil, nil],
-
0xfb88 => [0, 0, nil, "\332\210", nil, nil, nil],
-
0xfb89 => [0, 0, nil, "\332\210", nil, nil, nil],
-
0xfb8a => [0, 0, nil, "\332\230", nil, nil, nil],
-
0xfb8b => [0, 0, nil, "\332\230", nil, nil, nil],
-
0xfb8c => [0, 0, nil, "\332\221", nil, nil, nil],
-
0xfb8d => [0, 0, nil, "\332\221", nil, nil, nil],
-
0xfb8e => [0, 0, nil, "\332\251", nil, nil, nil],
-
0xfb8f => [0, 0, nil, "\332\251", nil, nil, nil],
-
0xfb90 => [0, 0, nil, "\332\251", nil, nil, nil],
-
0xfb91 => [0, 0, nil, "\332\251", nil, nil, nil],
-
0xfb92 => [0, 0, nil, "\332\257", nil, nil, nil],
-
0xfb93 => [0, 0, nil, "\332\257", nil, nil, nil],
-
0xfb94 => [0, 0, nil, "\332\257", nil, nil, nil],
-
0xfb95 => [0, 0, nil, "\332\257", nil, nil, nil],
-
0xfb96 => [0, 0, nil, "\332\263", nil, nil, nil],
-
0xfb97 => [0, 0, nil, "\332\263", nil, nil, nil],
-
0xfb98 => [0, 0, nil, "\332\263", nil, nil, nil],
-
0xfb99 => [0, 0, nil, "\332\263", nil, nil, nil],
-
0xfb9a => [0, 0, nil, "\332\261", nil, nil, nil],
-
0xfb9b => [0, 0, nil, "\332\261", nil, nil, nil],
-
0xfb9c => [0, 0, nil, "\332\261", nil, nil, nil],
-
0xfb9d => [0, 0, nil, "\332\261", nil, nil, nil],
-
0xfb9e => [0, 0, nil, "\332\272", nil, nil, nil],
-
0xfb9f => [0, 0, nil, "\332\272", nil, nil, nil],
-
0xfba0 => [0, 0, nil, "\332\273", nil, nil, nil],
-
0xfba1 => [0, 0, nil, "\332\273", nil, nil, nil],
-
0xfba2 => [0, 0, nil, "\332\273", nil, nil, nil],
-
0xfba3 => [0, 0, nil, "\332\273", nil, nil, nil],
-
0xfba4 => [0, 0, nil, "\333\200", nil, nil, nil],
-
0xfba5 => [0, 0, nil, "\333\200", nil, nil, nil],
-
0xfba6 => [0, 0, nil, "\333\201", nil, nil, nil],
-
0xfba7 => [0, 0, nil, "\333\201", nil, nil, nil],
-
0xfba8 => [0, 0, nil, "\333\201", nil, nil, nil],
-
0xfba9 => [0, 0, nil, "\333\201", nil, nil, nil],
-
0xfbaa => [0, 0, nil, "\332\276", nil, nil, nil],
-
0xfbab => [0, 0, nil, "\332\276", nil, nil, nil],
-
0xfbac => [0, 0, nil, "\332\276", nil, nil, nil],
-
0xfbad => [0, 0, nil, "\332\276", nil, nil, nil],
-
0xfbae => [0, 0, nil, "\333\222", nil, nil, nil],
-
0xfbaf => [0, 0, nil, "\333\222", nil, nil, nil],
-
0xfbb0 => [0, 0, nil, "\333\223", nil, nil, nil],
-
0xfbb1 => [0, 0, nil, "\333\223", nil, nil, nil],
-
0xfbd3 => [0, 0, nil, "\332\255", nil, nil, nil],
-
0xfbd4 => [0, 0, nil, "\332\255", nil, nil, nil],
-
0xfbd5 => [0, 0, nil, "\332\255", nil, nil, nil],
-
0xfbd6 => [0, 0, nil, "\332\255", nil, nil, nil],
-
0xfbd7 => [0, 0, nil, "\333\207", nil, nil, nil],
-
0xfbd8 => [0, 0, nil, "\333\207", nil, nil, nil],
-
0xfbd9 => [0, 0, nil, "\333\206", nil, nil, nil],
-
0xfbda => [0, 0, nil, "\333\206", nil, nil, nil],
-
0xfbdb => [0, 0, nil, "\333\210", nil, nil, nil],
-
0xfbdc => [0, 0, nil, "\333\210", nil, nil, nil],
-
0xfbdd => [0, 0, nil, "\331\267", nil, nil, nil],
-
0xfbde => [0, 0, nil, "\333\213", nil, nil, nil],
-
0xfbdf => [0, 0, nil, "\333\213", nil, nil, nil],
-
0xfbe0 => [0, 0, nil, "\333\205", nil, nil, nil],
-
0xfbe1 => [0, 0, nil, "\333\205", nil, nil, nil],
-
0xfbe2 => [0, 0, nil, "\333\211", nil, nil, nil],
-
0xfbe3 => [0, 0, nil, "\333\211", nil, nil, nil],
-
0xfbe4 => [0, 0, nil, "\333\220", nil, nil, nil],
-
0xfbe5 => [0, 0, nil, "\333\220", nil, nil, nil],
-
0xfbe6 => [0, 0, nil, "\333\220", nil, nil, nil],
-
0xfbe7 => [0, 0, nil, "\333\220", nil, nil, nil],
-
0xfbe8 => [0, 0, nil, "\331\211", nil, nil, nil],
-
0xfbe9 => [0, 0, nil, "\331\211", nil, nil, nil],
-
0xfbea => [0, 0, nil, "\330\246\330\247", nil, nil, nil],
-
0xfbeb => [0, 0, nil, "\330\246\330\247", nil, nil, nil],
-
0xfbec => [0, 0, nil, "\330\246\333\225", nil, nil, nil],
-
0xfbed => [0, 0, nil, "\330\246\333\225", nil, nil, nil],
-
0xfbee => [0, 0, nil, "\330\246\331\210", nil, nil, nil],
-
0xfbef => [0, 0, nil, "\330\246\331\210", nil, nil, nil],
-
0xfbf0 => [0, 0, nil, "\330\246\333\207", nil, nil, nil],
-
0xfbf1 => [0, 0, nil, "\330\246\333\207", nil, nil, nil],
-
0xfbf2 => [0, 0, nil, "\330\246\333\206", nil, nil, nil],
-
0xfbf3 => [0, 0, nil, "\330\246\333\206", nil, nil, nil],
-
0xfbf4 => [0, 0, nil, "\330\246\333\210", nil, nil, nil],
-
0xfbf5 => [0, 0, nil, "\330\246\333\210", nil, nil, nil],
-
0xfbf6 => [0, 0, nil, "\330\246\333\220", nil, nil, nil],
-
0xfbf7 => [0, 0, nil, "\330\246\333\220", nil, nil, nil],
-
0xfbf8 => [0, 0, nil, "\330\246\333\220", nil, nil, nil],
-
0xfbf9 => [0, 0, nil, "\330\246\331\211", nil, nil, nil],
-
0xfbfa => [0, 0, nil, "\330\246\331\211", nil, nil, nil],
-
0xfbfb => [0, 0, nil, "\330\246\331\211", nil, nil, nil],
-
0xfbfc => [0, 0, nil, "\333\214", nil, nil, nil],
-
0xfbfd => [0, 0, nil, "\333\214", nil, nil, nil],
-
0xfbfe => [0, 0, nil, "\333\214", nil, nil, nil],
-
0xfbff => [0, 0, nil, "\333\214", nil, nil, nil],
-
0xfc00 => [0, 0, nil, "\330\246\330\254", nil, nil, nil],
-
0xfc01 => [0, 0, nil, "\330\246\330\255", nil, nil, nil],
-
0xfc02 => [0, 0, nil, "\330\246\331\205", nil, nil, nil],
-
0xfc03 => [0, 0, nil, "\330\246\331\211", nil, nil, nil],
-
0xfc04 => [0, 0, nil, "\330\246\331\212", nil, nil, nil],
-
0xfc05 => [0, 0, nil, "\330\250\330\254", nil, nil, nil],
-
0xfc06 => [0, 0, nil, "\330\250\330\255", nil, nil, nil],
-
0xfc07 => [0, 0, nil, "\330\250\330\256", nil, nil, nil],
-
0xfc08 => [0, 0, nil, "\330\250\331\205", nil, nil, nil],
-
0xfc09 => [0, 0, nil, "\330\250\331\211", nil, nil, nil],
-
0xfc0a => [0, 0, nil, "\330\250\331\212", nil, nil, nil],
-
0xfc0b => [0, 0, nil, "\330\252\330\254", nil, nil, nil],
-
0xfc0c => [0, 0, nil, "\330\252\330\255", nil, nil, nil],
-
0xfc0d => [0, 0, nil, "\330\252\330\256", nil, nil, nil],
-
0xfc0e => [0, 0, nil, "\330\252\331\205", nil, nil, nil],
-
0xfc0f => [0, 0, nil, "\330\252\331\211", nil, nil, nil],
-
0xfc10 => [0, 0, nil, "\330\252\331\212", nil, nil, nil],
-
0xfc11 => [0, 0, nil, "\330\253\330\254", nil, nil, nil],
-
0xfc12 => [0, 0, nil, "\330\253\331\205", nil, nil, nil],
-
0xfc13 => [0, 0, nil, "\330\253\331\211", nil, nil, nil],
-
0xfc14 => [0, 0, nil, "\330\253\331\212", nil, nil, nil],
-
0xfc15 => [0, 0, nil, "\330\254\330\255", nil, nil, nil],
-
0xfc16 => [0, 0, nil, "\330\254\331\205", nil, nil, nil],
-
0xfc17 => [0, 0, nil, "\330\255\330\254", nil, nil, nil],
-
0xfc18 => [0, 0, nil, "\330\255\331\205", nil, nil, nil],
-
0xfc19 => [0, 0, nil, "\330\256\330\254", nil, nil, nil],
-
0xfc1a => [0, 0, nil, "\330\256\330\255", nil, nil, nil],
-
0xfc1b => [0, 0, nil, "\330\256\331\205", nil, nil, nil],
-
0xfc1c => [0, 0, nil, "\330\263\330\254", nil, nil, nil],
-
0xfc1d => [0, 0, nil, "\330\263\330\255", nil, nil, nil],
-
0xfc1e => [0, 0, nil, "\330\263\330\256", nil, nil, nil],
-
0xfc1f => [0, 0, nil, "\330\263\331\205", nil, nil, nil],
-
0xfc20 => [0, 0, nil, "\330\265\330\255", nil, nil, nil],
-
0xfc21 => [0, 0, nil, "\330\265\331\205", nil, nil, nil],
-
0xfc22 => [0, 0, nil, "\330\266\330\254", nil, nil, nil],
-
0xfc23 => [0, 0, nil, "\330\266\330\255", nil, nil, nil],
-
0xfc24 => [0, 0, nil, "\330\266\330\256", nil, nil, nil],
-
0xfc25 => [0, 0, nil, "\330\266\331\205", nil, nil, nil],
-
0xfc26 => [0, 0, nil, "\330\267\330\255", nil, nil, nil],
-
0xfc27 => [0, 0, nil, "\330\267\331\205", nil, nil, nil],
-
0xfc28 => [0, 0, nil, "\330\270\331\205", nil, nil, nil],
-
0xfc29 => [0, 0, nil, "\330\271\330\254", nil, nil, nil],
-
0xfc2a => [0, 0, nil, "\330\271\331\205", nil, nil, nil],
-
0xfc2b => [0, 0, nil, "\330\272\330\254", nil, nil, nil],
-
0xfc2c => [0, 0, nil, "\330\272\331\205", nil, nil, nil],
-
0xfc2d => [0, 0, nil, "\331\201\330\254", nil, nil, nil],
-
0xfc2e => [0, 0, nil, "\331\201\330\255", nil, nil, nil],
-
0xfc2f => [0, 0, nil, "\331\201\330\256", nil, nil, nil],
-
0xfc30 => [0, 0, nil, "\331\201\331\205", nil, nil, nil],
-
0xfc31 => [0, 0, nil, "\331\201\331\211", nil, nil, nil],
-
0xfc32 => [0, 0, nil, "\331\201\331\212", nil, nil, nil],
-
0xfc33 => [0, 0, nil, "\331\202\330\255", nil, nil, nil],
-
0xfc34 => [0, 0, nil, "\331\202\331\205", nil, nil, nil],
-
0xfc35 => [0, 0, nil, "\331\202\331\211", nil, nil, nil],
-
0xfc36 => [0, 0, nil, "\331\202\331\212", nil, nil, nil],
-
0xfc37 => [0, 0, nil, "\331\203\330\247", nil, nil, nil],
-
0xfc38 => [0, 0, nil, "\331\203\330\254", nil, nil, nil],
-
0xfc39 => [0, 0, nil, "\331\203\330\255", nil, nil, nil],
-
0xfc3a => [0, 0, nil, "\331\203\330\256", nil, nil, nil],
-
0xfc3b => [0, 0, nil, "\331\203\331\204", nil, nil, nil],
-
0xfc3c => [0, 0, nil, "\331\203\331\205", nil, nil, nil],
-
0xfc3d => [0, 0, nil, "\331\203\331\211", nil, nil, nil],
-
0xfc3e => [0, 0, nil, "\331\203\331\212", nil, nil, nil],
-
0xfc3f => [0, 0, nil, "\331\204\330\254", nil, nil, nil],
-
0xfc40 => [0, 0, nil, "\331\204\330\255", nil, nil, nil],
-
0xfc41 => [0, 0, nil, "\331\204\330\256", nil, nil, nil],
-
0xfc42 => [0, 0, nil, "\331\204\331\205", nil, nil, nil],
-
0xfc43 => [0, 0, nil, "\331\204\331\211", nil, nil, nil],
-
0xfc44 => [0, 0, nil, "\331\204\331\212", nil, nil, nil],
-
0xfc45 => [0, 0, nil, "\331\205\330\254", nil, nil, nil],
-
0xfc46 => [0, 0, nil, "\331\205\330\255", nil, nil, nil],
-
0xfc47 => [0, 0, nil, "\331\205\330\256", nil, nil, nil],
-
0xfc48 => [0, 0, nil, "\331\205\331\205", nil, nil, nil],
-
0xfc49 => [0, 0, nil, "\331\205\331\211", nil, nil, nil],
-
0xfc4a => [0, 0, nil, "\331\205\331\212", nil, nil, nil],
-
0xfc4b => [0, 0, nil, "\331\206\330\254", nil, nil, nil],
-
0xfc4c => [0, 0, nil, "\331\206\330\255", nil, nil, nil],
-
0xfc4d => [0, 0, nil, "\331\206\330\256", nil, nil, nil],
-
0xfc4e => [0, 0, nil, "\331\206\331\205", nil, nil, nil],
-
0xfc4f => [0, 0, nil, "\331\206\331\211", nil, nil, nil],
-
0xfc50 => [0, 0, nil, "\331\206\331\212", nil, nil, nil],
-
0xfc51 => [0, 0, nil, "\331\207\330\254", nil, nil, nil],
-
0xfc52 => [0, 0, nil, "\331\207\331\205", nil, nil, nil],
-
0xfc53 => [0, 0, nil, "\331\207\331\211", nil, nil, nil],
-
0xfc54 => [0, 0, nil, "\331\207\331\212", nil, nil, nil],
-
0xfc55 => [0, 0, nil, "\331\212\330\254", nil, nil, nil],
-
0xfc56 => [0, 0, nil, "\331\212\330\255", nil, nil, nil],
-
0xfc57 => [0, 0, nil, "\331\212\330\256", nil, nil, nil],
-
0xfc58 => [0, 0, nil, "\331\212\331\205", nil, nil, nil],
-
0xfc59 => [0, 0, nil, "\331\212\331\211", nil, nil, nil],
-
0xfc5a => [0, 0, nil, "\331\212\331\212", nil, nil, nil],
-
0xfc5b => [0, 0, nil, "\330\260\331\260", nil, nil, nil],
-
0xfc5c => [0, 0, nil, "\330\261\331\260", nil, nil, nil],
-
0xfc5d => [0, 0, nil, "\331\211\331\260", nil, nil, nil],
-
0xfc5e => [0, 0, nil, " \331\214\331\221", nil, nil, nil],
-
0xfc5f => [0, 0, nil, " \331\215\331\221", nil, nil, nil],
-
0xfc60 => [0, 0, nil, " \331\216\331\221", nil, nil, nil],
-
0xfc61 => [0, 0, nil, " \331\217\331\221", nil, nil, nil],
-
0xfc62 => [0, 0, nil, " \331\220\331\221", nil, nil, nil],
-
0xfc63 => [0, 0, nil, " \331\221\331\260", nil, nil, nil],
-
0xfc64 => [0, 0, nil, "\330\246\330\261", nil, nil, nil],
-
0xfc65 => [0, 0, nil, "\330\246\330\262", nil, nil, nil],
-
0xfc66 => [0, 0, nil, "\330\246\331\205", nil, nil, nil],
-
0xfc67 => [0, 0, nil, "\330\246\331\206", nil, nil, nil],
-
0xfc68 => [0, 0, nil, "\330\246\331\211", nil, nil, nil],
-
0xfc69 => [0, 0, nil, "\330\246\331\212", nil, nil, nil],
-
0xfc6a => [0, 0, nil, "\330\250\330\261", nil, nil, nil],
-
0xfc6b => [0, 0, nil, "\330\250\330\262", nil, nil, nil],
-
0xfc6c => [0, 0, nil, "\330\250\331\205", nil, nil, nil],
-
0xfc6d => [0, 0, nil, "\330\250\331\206", nil, nil, nil],
-
0xfc6e => [0, 0, nil, "\330\250\331\211", nil, nil, nil],
-
0xfc6f => [0, 0, nil, "\330\250\331\212", nil, nil, nil],
-
0xfc70 => [0, 0, nil, "\330\252\330\261", nil, nil, nil],
-
0xfc71 => [0, 0, nil, "\330\252\330\262", nil, nil, nil],
-
0xfc72 => [0, 0, nil, "\330\252\331\205", nil, nil, nil],
-
0xfc73 => [0, 0, nil, "\330\252\331\206", nil, nil, nil],
-
0xfc74 => [0, 0, nil, "\330\252\331\211", nil, nil, nil],
-
0xfc75 => [0, 0, nil, "\330\252\331\212", nil, nil, nil],
-
0xfc76 => [0, 0, nil, "\330\253\330\261", nil, nil, nil],
-
0xfc77 => [0, 0, nil, "\330\253\330\262", nil, nil, nil],
-
0xfc78 => [0, 0, nil, "\330\253\331\205", nil, nil, nil],
-
0xfc79 => [0, 0, nil, "\330\253\331\206", nil, nil, nil],
-
0xfc7a => [0, 0, nil, "\330\253\331\211", nil, nil, nil],
-
0xfc7b => [0, 0, nil, "\330\253\331\212", nil, nil, nil],
-
0xfc7c => [0, 0, nil, "\331\201\331\211", nil, nil, nil],
-
0xfc7d => [0, 0, nil, "\331\201\331\212", nil, nil, nil],
-
0xfc7e => [0, 0, nil, "\331\202\331\211", nil, nil, nil],
-
0xfc7f => [0, 0, nil, "\331\202\331\212", nil, nil, nil],
-
0xfc80 => [0, 0, nil, "\331\203\330\247", nil, nil, nil],
-
0xfc81 => [0, 0, nil, "\331\203\331\204", nil, nil, nil],
-
0xfc82 => [0, 0, nil, "\331\203\331\205", nil, nil, nil],
-
0xfc83 => [0, 0, nil, "\331\203\331\211", nil, nil, nil],
-
0xfc84 => [0, 0, nil, "\331\203\331\212", nil, nil, nil],
-
0xfc85 => [0, 0, nil, "\331\204\331\205", nil, nil, nil],
-
0xfc86 => [0, 0, nil, "\331\204\331\211", nil, nil, nil],
-
0xfc87 => [0, 0, nil, "\331\204\331\212", nil, nil, nil],
-
0xfc88 => [0, 0, nil, "\331\205\330\247", nil, nil, nil],
-
0xfc89 => [0, 0, nil, "\331\205\331\205", nil, nil, nil],
-
0xfc8a => [0, 0, nil, "\331\206\330\261", nil, nil, nil],
-
0xfc8b => [0, 0, nil, "\331\206\330\262", nil, nil, nil],
-
0xfc8c => [0, 0, nil, "\331\206\331\205", nil, nil, nil],
-
0xfc8d => [0, 0, nil, "\331\206\331\206", nil, nil, nil],
-
0xfc8e => [0, 0, nil, "\331\206\331\211", nil, nil, nil],
-
0xfc8f => [0, 0, nil, "\331\206\331\212", nil, nil, nil],
-
0xfc90 => [0, 0, nil, "\331\211\331\260", nil, nil, nil],
-
0xfc91 => [0, 0, nil, "\331\212\330\261", nil, nil, nil],
-
0xfc92 => [0, 0, nil, "\331\212\330\262", nil, nil, nil],
-
0xfc93 => [0, 0, nil, "\331\212\331\205", nil, nil, nil],
-
0xfc94 => [0, 0, nil, "\331\212\331\206", nil, nil, nil],
-
0xfc95 => [0, 0, nil, "\331\212\331\211", nil, nil, nil],
-
0xfc96 => [0, 0, nil, "\331\212\331\212", nil, nil, nil],
-
0xfc97 => [0, 0, nil, "\330\246\330\254", nil, nil, nil],
-
0xfc98 => [0, 0, nil, "\330\246\330\255", nil, nil, nil],
-
0xfc99 => [0, 0, nil, "\330\246\330\256", nil, nil, nil],
-
0xfc9a => [0, 0, nil, "\330\246\331\205", nil, nil, nil],
-
0xfc9b => [0, 0, nil, "\330\246\331\207", nil, nil, nil],
-
0xfc9c => [0, 0, nil, "\330\250\330\254", nil, nil, nil],
-
0xfc9d => [0, 0, nil, "\330\250\330\255", nil, nil, nil],
-
0xfc9e => [0, 0, nil, "\330\250\330\256", nil, nil, nil],
-
0xfc9f => [0, 0, nil, "\330\250\331\205", nil, nil, nil],
-
0xfca0 => [0, 0, nil, "\330\250\331\207", nil, nil, nil],
-
0xfca1 => [0, 0, nil, "\330\252\330\254", nil, nil, nil],
-
0xfca2 => [0, 0, nil, "\330\252\330\255", nil, nil, nil],
-
0xfca3 => [0, 0, nil, "\330\252\330\256", nil, nil, nil],
-
0xfca4 => [0, 0, nil, "\330\252\331\205", nil, nil, nil],
-
0xfca5 => [0, 0, nil, "\330\252\331\207", nil, nil, nil],
-
0xfca6 => [0, 0, nil, "\330\253\331\205", nil, nil, nil],
-
0xfca7 => [0, 0, nil, "\330\254\330\255", nil, nil, nil],
-
0xfca8 => [0, 0, nil, "\330\254\331\205", nil, nil, nil],
-
0xfca9 => [0, 0, nil, "\330\255\330\254", nil, nil, nil],
-
0xfcaa => [0, 0, nil, "\330\255\331\205", nil, nil, nil],
-
0xfcab => [0, 0, nil, "\330\256\330\254", nil, nil, nil],
-
0xfcac => [0, 0, nil, "\330\256\331\205", nil, nil, nil],
-
0xfcad => [0, 0, nil, "\330\263\330\254", nil, nil, nil],
-
0xfcae => [0, 0, nil, "\330\263\330\255", nil, nil, nil],
-
0xfcaf => [0, 0, nil, "\330\263\330\256", nil, nil, nil],
-
0xfcb0 => [0, 0, nil, "\330\263\331\205", nil, nil, nil],
-
0xfcb1 => [0, 0, nil, "\330\265\330\255", nil, nil, nil],
-
0xfcb2 => [0, 0, nil, "\330\265\330\256", nil, nil, nil],
-
0xfcb3 => [0, 0, nil, "\330\265\331\205", nil, nil, nil],
-
0xfcb4 => [0, 0, nil, "\330\266\330\254", nil, nil, nil],
-
0xfcb5 => [0, 0, nil, "\330\266\330\255", nil, nil, nil],
-
0xfcb6 => [0, 0, nil, "\330\266\330\256", nil, nil, nil],
-
0xfcb7 => [0, 0, nil, "\330\266\331\205", nil, nil, nil],
-
0xfcb8 => [0, 0, nil, "\330\267\330\255", nil, nil, nil],
-
0xfcb9 => [0, 0, nil, "\330\270\331\205", nil, nil, nil],
-
0xfcba => [0, 0, nil, "\330\271\330\254", nil, nil, nil],
-
0xfcbb => [0, 0, nil, "\330\271\331\205", nil, nil, nil],
-
0xfcbc => [0, 0, nil, "\330\272\330\254", nil, nil, nil],
-
0xfcbd => [0, 0, nil, "\330\272\331\205", nil, nil, nil],
-
0xfcbe => [0, 0, nil, "\331\201\330\254", nil, nil, nil],
-
0xfcbf => [0, 0, nil, "\331\201\330\255", nil, nil, nil],
-
0xfcc0 => [0, 0, nil, "\331\201\330\256", nil, nil, nil],
-
0xfcc1 => [0, 0, nil, "\331\201\331\205", nil, nil, nil],
-
0xfcc2 => [0, 0, nil, "\331\202\330\255", nil, nil, nil],
-
0xfcc3 => [0, 0, nil, "\331\202\331\205", nil, nil, nil],
-
0xfcc4 => [0, 0, nil, "\331\203\330\254", nil, nil, nil],
-
0xfcc5 => [0, 0, nil, "\331\203\330\255", nil, nil, nil],
-
0xfcc6 => [0, 0, nil, "\331\203\330\256", nil, nil, nil],
-
0xfcc7 => [0, 0, nil, "\331\203\331\204", nil, nil, nil],
-
0xfcc8 => [0, 0, nil, "\331\203\331\205", nil, nil, nil],
-
0xfcc9 => [0, 0, nil, "\331\204\330\254", nil, nil, nil],
-
0xfcca => [0, 0, nil, "\331\204\330\255", nil, nil, nil],
-
0xfccb => [0, 0, nil, "\331\204\330\256", nil, nil, nil],
-
0xfccc => [0, 0, nil, "\331\204\331\205", nil, nil, nil],
-
0xfccd => [0, 0, nil, "\331\204\331\207", nil, nil, nil],
-
0xfcce => [0, 0, nil, "\331\205\330\254", nil, nil, nil],
-
0xfccf => [0, 0, nil, "\331\205\330\255", nil, nil, nil],
-
0xfcd0 => [0, 0, nil, "\331\205\330\256", nil, nil, nil],
-
0xfcd1 => [0, 0, nil, "\331\205\331\205", nil, nil, nil],
-
0xfcd2 => [0, 0, nil, "\331\206\330\254", nil, nil, nil],
-
0xfcd3 => [0, 0, nil, "\331\206\330\255", nil, nil, nil],
-
0xfcd4 => [0, 0, nil, "\331\206\330\256", nil, nil, nil],
-
0xfcd5 => [0, 0, nil, "\331\206\331\205", nil, nil, nil],
-
0xfcd6 => [0, 0, nil, "\331\206\331\207", nil, nil, nil],
-
0xfcd7 => [0, 0, nil, "\331\207\330\254", nil, nil, nil],
-
0xfcd8 => [0, 0, nil, "\331\207\331\205", nil, nil, nil],
-
0xfcd9 => [0, 0, nil, "\331\207\331\260", nil, nil, nil],
-
0xfcda => [0, 0, nil, "\331\212\330\254", nil, nil, nil],
-
0xfcdb => [0, 0, nil, "\331\212\330\255", nil, nil, nil],
-
0xfcdc => [0, 0, nil, "\331\212\330\256", nil, nil, nil],
-
0xfcdd => [0, 0, nil, "\331\212\331\205", nil, nil, nil],
-
0xfcde => [0, 0, nil, "\331\212\331\207", nil, nil, nil],
-
0xfcdf => [0, 0, nil, "\330\246\331\205", nil, nil, nil],
-
0xfce0 => [0, 0, nil, "\330\246\331\207", nil, nil, nil],
-
0xfce1 => [0, 0, nil, "\330\250\331\205", nil, nil, nil],
-
0xfce2 => [0, 0, nil, "\330\250\331\207", nil, nil, nil],
-
0xfce3 => [0, 0, nil, "\330\252\331\205", nil, nil, nil],
-
0xfce4 => [0, 0, nil, "\330\252\331\207", nil, nil, nil],
-
0xfce5 => [0, 0, nil, "\330\253\331\205", nil, nil, nil],
-
0xfce6 => [0, 0, nil, "\330\253\331\207", nil, nil, nil],
-
0xfce7 => [0, 0, nil, "\330\263\331\205", nil, nil, nil],
-
0xfce8 => [0, 0, nil, "\330\263\331\207", nil, nil, nil],
-
0xfce9 => [0, 0, nil, "\330\264\331\205", nil, nil, nil],
-
0xfcea => [0, 0, nil, "\330\264\331\207", nil, nil, nil],
-
0xfceb => [0, 0, nil, "\331\203\331\204", nil, nil, nil],
-
0xfcec => [0, 0, nil, "\331\203\331\205", nil, nil, nil],
-
0xfced => [0, 0, nil, "\331\204\331\205", nil, nil, nil],
-
0xfcee => [0, 0, nil, "\331\206\331\205", nil, nil, nil],
-
0xfcef => [0, 0, nil, "\331\206\331\207", nil, nil, nil],
-
0xfcf0 => [0, 0, nil, "\331\212\331\205", nil, nil, nil],
-
0xfcf1 => [0, 0, nil, "\331\212\331\207", nil, nil, nil],
-
0xfcf2 => [0, 0, nil, "\331\200\331\216\331\221", nil, nil, nil],
-
0xfcf3 => [0, 0, nil, "\331\200\331\217\331\221", nil, nil, nil],
-
0xfcf4 => [0, 0, nil, "\331\200\331\220\331\221", nil, nil, nil],
-
0xfcf5 => [0, 0, nil, "\330\267\331\211", nil, nil, nil],
-
0xfcf6 => [0, 0, nil, "\330\267\331\212", nil, nil, nil],
-
0xfcf7 => [0, 0, nil, "\330\271\331\211", nil, nil, nil],
-
0xfcf8 => [0, 0, nil, "\330\271\331\212", nil, nil, nil],
-
0xfcf9 => [0, 0, nil, "\330\272\331\211", nil, nil, nil],
-
0xfcfa => [0, 0, nil, "\330\272\331\212", nil, nil, nil],
-
0xfcfb => [0, 0, nil, "\330\263\331\211", nil, nil, nil],
-
0xfcfc => [0, 0, nil, "\330\263\331\212", nil, nil, nil],
-
0xfcfd => [0, 0, nil, "\330\264\331\211", nil, nil, nil],
-
0xfcfe => [0, 0, nil, "\330\264\331\212", nil, nil, nil],
-
0xfcff => [0, 0, nil, "\330\255\331\211", nil, nil, nil],
-
0xfd00 => [0, 0, nil, "\330\255\331\212", nil, nil, nil],
-
0xfd01 => [0, 0, nil, "\330\254\331\211", nil, nil, nil],
-
0xfd02 => [0, 0, nil, "\330\254\331\212", nil, nil, nil],
-
0xfd03 => [0, 0, nil, "\330\256\331\211", nil, nil, nil],
-
0xfd04 => [0, 0, nil, "\330\256\331\212", nil, nil, nil],
-
0xfd05 => [0, 0, nil, "\330\265\331\211", nil, nil, nil],
-
0xfd06 => [0, 0, nil, "\330\265\331\212", nil, nil, nil],
-
0xfd07 => [0, 0, nil, "\330\266\331\211", nil, nil, nil],
-
0xfd08 => [0, 0, nil, "\330\266\331\212", nil, nil, nil],
-
0xfd09 => [0, 0, nil, "\330\264\330\254", nil, nil, nil],
-
0xfd0a => [0, 0, nil, "\330\264\330\255", nil, nil, nil],
-
0xfd0b => [0, 0, nil, "\330\264\330\256", nil, nil, nil],
-
0xfd0c => [0, 0, nil, "\330\264\331\205", nil, nil, nil],
-
0xfd0d => [0, 0, nil, "\330\264\330\261", nil, nil, nil],
-
0xfd0e => [0, 0, nil, "\330\263\330\261", nil, nil, nil],
-
0xfd0f => [0, 0, nil, "\330\265\330\261", nil, nil, nil],
-
0xfd10 => [0, 0, nil, "\330\266\330\261", nil, nil, nil],
-
0xfd11 => [0, 0, nil, "\330\267\331\211", nil, nil, nil],
-
0xfd12 => [0, 0, nil, "\330\267\331\212", nil, nil, nil],
-
0xfd13 => [0, 0, nil, "\330\271\331\211", nil, nil, nil],
-
0xfd14 => [0, 0, nil, "\330\271\331\212", nil, nil, nil],
-
0xfd15 => [0, 0, nil, "\330\272\331\211", nil, nil, nil],
-
0xfd16 => [0, 0, nil, "\330\272\331\212", nil, nil, nil],
-
0xfd17 => [0, 0, nil, "\330\263\331\211", nil, nil, nil],
-
0xfd18 => [0, 0, nil, "\330\263\331\212", nil, nil, nil],
-
0xfd19 => [0, 0, nil, "\330\264\331\211", nil, nil, nil],
-
0xfd1a => [0, 0, nil, "\330\264\331\212", nil, nil, nil],
-
0xfd1b => [0, 0, nil, "\330\255\331\211", nil, nil, nil],
-
0xfd1c => [0, 0, nil, "\330\255\331\212", nil, nil, nil],
-
0xfd1d => [0, 0, nil, "\330\254\331\211", nil, nil, nil],
-
0xfd1e => [0, 0, nil, "\330\254\331\212", nil, nil, nil],
-
0xfd1f => [0, 0, nil, "\330\256\331\211", nil, nil, nil],
-
0xfd20 => [0, 0, nil, "\330\256\331\212", nil, nil, nil],
-
0xfd21 => [0, 0, nil, "\330\265\331\211", nil, nil, nil],
-
0xfd22 => [0, 0, nil, "\330\265\331\212", nil, nil, nil],
-
0xfd23 => [0, 0, nil, "\330\266\331\211", nil, nil, nil],
-
0xfd24 => [0, 0, nil, "\330\266\331\212", nil, nil, nil],
-
0xfd25 => [0, 0, nil, "\330\264\330\254", nil, nil, nil],
-
0xfd26 => [0, 0, nil, "\330\264\330\255", nil, nil, nil],
-
0xfd27 => [0, 0, nil, "\330\264\330\256", nil, nil, nil],
-
0xfd28 => [0, 0, nil, "\330\264\331\205", nil, nil, nil],
-
0xfd29 => [0, 0, nil, "\330\264\330\261", nil, nil, nil],
-
0xfd2a => [0, 0, nil, "\330\263\330\261", nil, nil, nil],
-
0xfd2b => [0, 0, nil, "\330\265\330\261", nil, nil, nil],
-
0xfd2c => [0, 0, nil, "\330\266\330\261", nil, nil, nil],
-
0xfd2d => [0, 0, nil, "\330\264\330\254", nil, nil, nil],
-
0xfd2e => [0, 0, nil, "\330\264\330\255", nil, nil, nil],
-
0xfd2f => [0, 0, nil, "\330\264\330\256", nil, nil, nil],
-
0xfd30 => [0, 0, nil, "\330\264\331\205", nil, nil, nil],
-
0xfd31 => [0, 0, nil, "\330\263\331\207", nil, nil, nil],
-
0xfd32 => [0, 0, nil, "\330\264\331\207", nil, nil, nil],
-
0xfd33 => [0, 0, nil, "\330\267\331\205", nil, nil, nil],
-
0xfd34 => [0, 0, nil, "\330\263\330\254", nil, nil, nil],
-
0xfd35 => [0, 0, nil, "\330\263\330\255", nil, nil, nil],
-
0xfd36 => [0, 0, nil, "\330\263\330\256", nil, nil, nil],
-
0xfd37 => [0, 0, nil, "\330\264\330\254", nil, nil, nil],
-
0xfd38 => [0, 0, nil, "\330\264\330\255", nil, nil, nil],
-
0xfd39 => [0, 0, nil, "\330\264\330\256", nil, nil, nil],
-
0xfd3a => [0, 0, nil, "\330\267\331\205", nil, nil, nil],
-
0xfd3b => [0, 0, nil, "\330\270\331\205", nil, nil, nil],
-
0xfd3c => [0, 0, nil, "\330\247\331\213", nil, nil, nil],
-
0xfd3d => [0, 0, nil, "\330\247\331\213", nil, nil, nil],
-
0xfd50 => [0, 0, nil, "\330\252\330\254\331\205", nil, nil, nil],
-
0xfd51 => [0, 0, nil, "\330\252\330\255\330\254", nil, nil, nil],
-
0xfd52 => [0, 0, nil, "\330\252\330\255\330\254", nil, nil, nil],
-
0xfd53 => [0, 0, nil, "\330\252\330\255\331\205", nil, nil, nil],
-
0xfd54 => [0, 0, nil, "\330\252\330\256\331\205", nil, nil, nil],
-
0xfd55 => [0, 0, nil, "\330\252\331\205\330\254", nil, nil, nil],
-
0xfd56 => [0, 0, nil, "\330\252\331\205\330\255", nil, nil, nil],
-
0xfd57 => [0, 0, nil, "\330\252\331\205\330\256", nil, nil, nil],
-
0xfd58 => [0, 0, nil, "\330\254\331\205\330\255", nil, nil, nil],
-
0xfd59 => [0, 0, nil, "\330\254\331\205\330\255", nil, nil, nil],
-
0xfd5a => [0, 0, nil, "\330\255\331\205\331\212", nil, nil, nil],
-
0xfd5b => [0, 0, nil, "\330\255\331\205\331\211", nil, nil, nil],
-
0xfd5c => [0, 0, nil, "\330\263\330\255\330\254", nil, nil, nil],
-
0xfd5d => [0, 0, nil, "\330\263\330\254\330\255", nil, nil, nil],
-
0xfd5e => [0, 0, nil, "\330\263\330\254\331\211", nil, nil, nil],
-
0xfd5f => [0, 0, nil, "\330\263\331\205\330\255", nil, nil, nil],
-
0xfd60 => [0, 0, nil, "\330\263\331\205\330\255", nil, nil, nil],
-
0xfd61 => [0, 0, nil, "\330\263\331\205\330\254", nil, nil, nil],
-
0xfd62 => [0, 0, nil, "\330\263\331\205\331\205", nil, nil, nil],
-
0xfd63 => [0, 0, nil, "\330\263\331\205\331\205", nil, nil, nil],
-
0xfd64 => [0, 0, nil, "\330\265\330\255\330\255", nil, nil, nil],
-
0xfd65 => [0, 0, nil, "\330\265\330\255\330\255", nil, nil, nil],
-
0xfd66 => [0, 0, nil, "\330\265\331\205\331\205", nil, nil, nil],
-
0xfd67 => [0, 0, nil, "\330\264\330\255\331\205", nil, nil, nil],
-
0xfd68 => [0, 0, nil, "\330\264\330\255\331\205", nil, nil, nil],
-
0xfd69 => [0, 0, nil, "\330\264\330\254\331\212", nil, nil, nil],
-
0xfd6a => [0, 0, nil, "\330\264\331\205\330\256", nil, nil, nil],
-
0xfd6b => [0, 0, nil, "\330\264\331\205\330\256", nil, nil, nil],
-
0xfd6c => [0, 0, nil, "\330\264\331\205\331\205", nil, nil, nil],
-
0xfd6d => [0, 0, nil, "\330\264\331\205\331\205", nil, nil, nil],
-
0xfd6e => [0, 0, nil, "\330\266\330\255\331\211", nil, nil, nil],
-
0xfd6f => [0, 0, nil, "\330\266\330\256\331\205", nil, nil, nil],
-
0xfd70 => [0, 0, nil, "\330\266\330\256\331\205", nil, nil, nil],
-
0xfd71 => [0, 0, nil, "\330\267\331\205\330\255", nil, nil, nil],
-
0xfd72 => [0, 0, nil, "\330\267\331\205\330\255", nil, nil, nil],
-
0xfd73 => [0, 0, nil, "\330\267\331\205\331\205", nil, nil, nil],
-
0xfd74 => [0, 0, nil, "\330\267\331\205\331\212", nil, nil, nil],
-
0xfd75 => [0, 0, nil, "\330\271\330\254\331\205", nil, nil, nil],
-
0xfd76 => [0, 0, nil, "\330\271\331\205\331\205", nil, nil, nil],
-
0xfd77 => [0, 0, nil, "\330\271\331\205\331\205", nil, nil, nil],
-
0xfd78 => [0, 0, nil, "\330\271\331\205\331\211", nil, nil, nil],
-
0xfd79 => [0, 0, nil, "\330\272\331\205\331\205", nil, nil, nil],
-
0xfd7a => [0, 0, nil, "\330\272\331\205\331\212", nil, nil, nil],
-
0xfd7b => [0, 0, nil, "\330\272\331\205\331\211", nil, nil, nil],
-
0xfd7c => [0, 0, nil, "\331\201\330\256\331\205", nil, nil, nil],
-
0xfd7d => [0, 0, nil, "\331\201\330\256\331\205", nil, nil, nil],
-
0xfd7e => [0, 0, nil, "\331\202\331\205\330\255", nil, nil, nil],
-
0xfd7f => [0, 0, nil, "\331\202\331\205\331\205", nil, nil, nil],
-
0xfd80 => [0, 0, nil, "\331\204\330\255\331\205", nil, nil, nil],
-
0xfd81 => [0, 0, nil, "\331\204\330\255\331\212", nil, nil, nil],
-
0xfd82 => [0, 0, nil, "\331\204\330\255\331\211", nil, nil, nil],
-
0xfd83 => [0, 0, nil, "\331\204\330\254\330\254", nil, nil, nil],
-
0xfd84 => [0, 0, nil, "\331\204\330\254\330\254", nil, nil, nil],
-
0xfd85 => [0, 0, nil, "\331\204\330\256\331\205", nil, nil, nil],
-
0xfd86 => [0, 0, nil, "\331\204\330\256\331\205", nil, nil, nil],
-
0xfd87 => [0, 0, nil, "\331\204\331\205\330\255", nil, nil, nil],
-
0xfd88 => [0, 0, nil, "\331\204\331\205\330\255", nil, nil, nil],
-
0xfd89 => [0, 0, nil, "\331\205\330\255\330\254", nil, nil, nil],
-
0xfd8a => [0, 0, nil, "\331\205\330\255\331\205", nil, nil, nil],
-
0xfd8b => [0, 0, nil, "\331\205\330\255\331\212", nil, nil, nil],
-
0xfd8c => [0, 0, nil, "\331\205\330\254\330\255", nil, nil, nil],
-
0xfd8d => [0, 0, nil, "\331\205\330\254\331\205", nil, nil, nil],
-
0xfd8e => [0, 0, nil, "\331\205\330\256\330\254", nil, nil, nil],
-
0xfd8f => [0, 0, nil, "\331\205\330\256\331\205", nil, nil, nil],
-
0xfd92 => [0, 0, nil, "\331\205\330\254\330\256", nil, nil, nil],
-
0xfd93 => [0, 0, nil, "\331\207\331\205\330\254", nil, nil, nil],
-
0xfd94 => [0, 0, nil, "\331\207\331\205\331\205", nil, nil, nil],
-
0xfd95 => [0, 0, nil, "\331\206\330\255\331\205", nil, nil, nil],
-
0xfd96 => [0, 0, nil, "\331\206\330\255\331\211", nil, nil, nil],
-
0xfd97 => [0, 0, nil, "\331\206\330\254\331\205", nil, nil, nil],
-
0xfd98 => [0, 0, nil, "\331\206\330\254\331\205", nil, nil, nil],
-
0xfd99 => [0, 0, nil, "\331\206\330\254\331\211", nil, nil, nil],
-
0xfd9a => [0, 0, nil, "\331\206\331\205\331\212", nil, nil, nil],
-
0xfd9b => [0, 0, nil, "\331\206\331\205\331\211", nil, nil, nil],
-
0xfd9c => [0, 0, nil, "\331\212\331\205\331\205", nil, nil, nil],
-
0xfd9d => [0, 0, nil, "\331\212\331\205\331\205", nil, nil, nil],
-
0xfd9e => [0, 0, nil, "\330\250\330\256\331\212", nil, nil, nil],
-
0xfd9f => [0, 0, nil, "\330\252\330\254\331\212", nil, nil, nil],
-
0xfda0 => [0, 0, nil, "\330\252\330\254\331\211", nil, nil, nil],
-
0xfda1 => [0, 0, nil, "\330\252\330\256\331\212", nil, nil, nil],
-
0xfda2 => [0, 0, nil, "\330\252\330\256\331\211", nil, nil, nil],
-
0xfda3 => [0, 0, nil, "\330\252\331\205\331\212", nil, nil, nil],
-
0xfda4 => [0, 0, nil, "\330\252\331\205\331\211", nil, nil, nil],
-
0xfda5 => [0, 0, nil, "\330\254\331\205\331\212", nil, nil, nil],
-
0xfda6 => [0, 0, nil, "\330\254\330\255\331\211", nil, nil, nil],
-
0xfda7 => [0, 0, nil, "\330\254\331\205\331\211", nil, nil, nil],
-
0xfda8 => [0, 0, nil, "\330\263\330\256\331\211", nil, nil, nil],
-
0xfda9 => [0, 0, nil, "\330\265\330\255\331\212", nil, nil, nil],
-
0xfdaa => [0, 0, nil, "\330\264\330\255\331\212", nil, nil, nil],
-
0xfdab => [0, 0, nil, "\330\266\330\255\331\212", nil, nil, nil],
-
0xfdac => [0, 0, nil, "\331\204\330\254\331\212", nil, nil, nil],
-
0xfdad => [0, 0, nil, "\331\204\331\205\331\212", nil, nil, nil],
-
0xfdae => [0, 0, nil, "\331\212\330\255\331\212", nil, nil, nil],
-
0xfdaf => [0, 0, nil, "\331\212\330\254\331\212", nil, nil, nil],
-
0xfdb0 => [0, 0, nil, "\331\212\331\205\331\212", nil, nil, nil],
-
0xfdb1 => [0, 0, nil, "\331\205\331\205\331\212", nil, nil, nil],
-
0xfdb2 => [0, 0, nil, "\331\202\331\205\331\212", nil, nil, nil],
-
0xfdb3 => [0, 0, nil, "\331\206\330\255\331\212", nil, nil, nil],
-
0xfdb4 => [0, 0, nil, "\331\202\331\205\330\255", nil, nil, nil],
-
0xfdb5 => [0, 0, nil, "\331\204\330\255\331\205", nil, nil, nil],
-
0xfdb6 => [0, 0, nil, "\330\271\331\205\331\212", nil, nil, nil],
-
0xfdb7 => [0, 0, nil, "\331\203\331\205\331\212", nil, nil, nil],
-
0xfdb8 => [0, 0, nil, "\331\206\330\254\330\255", nil, nil, nil],
-
0xfdb9 => [0, 0, nil, "\331\205\330\256\331\212", nil, nil, nil],
-
0xfdba => [0, 0, nil, "\331\204\330\254\331\205", nil, nil, nil],
-
0xfdbb => [0, 0, nil, "\331\203\331\205\331\205", nil, nil, nil],
-
0xfdbc => [0, 0, nil, "\331\204\330\254\331\205", nil, nil, nil],
-
0xfdbd => [0, 0, nil, "\331\206\330\254\330\255", nil, nil, nil],
-
0xfdbe => [0, 0, nil, "\330\254\330\255\331\212", nil, nil, nil],
-
0xfdbf => [0, 0, nil, "\330\255\330\254\331\212", nil, nil, nil],
-
0xfdc0 => [0, 0, nil, "\331\205\330\254\331\212", nil, nil, nil],
-
0xfdc1 => [0, 0, nil, "\331\201\331\205\331\212", nil, nil, nil],
-
0xfdc2 => [0, 0, nil, "\330\250\330\255\331\212", nil, nil, nil],
-
0xfdc3 => [0, 0, nil, "\331\203\331\205\331\205", nil, nil, nil],
-
0xfdc4 => [0, 0, nil, "\330\271\330\254\331\205", nil, nil, nil],
-
0xfdc5 => [0, 0, nil, "\330\265\331\205\331\205", nil, nil, nil],
-
0xfdc6 => [0, 0, nil, "\330\263\330\256\331\212", nil, nil, nil],
-
0xfdc7 => [0, 0, nil, "\331\206\330\254\331\212", nil, nil, nil],
-
0xfdf0 => [0, 0, nil, "\330\265\331\204\333\222", nil, nil, nil],
-
0xfdf1 => [0, 0, nil, "\331\202\331\204\333\222", nil, nil, nil],
-
0xfdf2 => [0, 0, nil, "\330\247\331\204\331\204\331\207", nil, nil, nil],
-
0xfdf3 => [0, 0, nil, "\330\247\331\203\330\250\330\261", nil, nil, nil],
-
0xfdf4 => [0, 0, nil, "\331\205\330\255\331\205\330\257", nil, nil, nil],
-
0xfdf5 => [0, 0, nil, "\330\265\331\204\330\271\331\205", nil, nil, nil],
-
0xfdf6 => [0, 0, nil, "\330\261\330\263\331\210\331\204", nil, nil, nil],
-
0xfdf7 => [0, 0, nil, "\330\271\331\204\331\212\331\207", nil, nil, nil],
-
0xfdf8 => [0, 0, nil, "\331\210\330\263\331\204\331\205", nil, nil, nil],
-
0xfdf9 => [0, 0, nil, "\330\265\331\204\331\211", nil, nil, nil],
-
0xfdfa => [0, 0, nil, "\330\265\331\204\331\211 \330\247\331\204\331\204\331\207 \330\271\331\204\331\212\331\207 \331\210\330\263\331\204\331\205", nil, nil, nil],
-
0xfdfb => [0, 0, nil, "\330\254\331\204 \330\254\331\204\330\247\331\204\331\207", nil, nil, nil],
-
0xfe20 => [230, 0, nil, nil, nil, nil, nil],
-
0xfe21 => [230, 0, nil, nil, nil, nil, nil],
-
0xfe22 => [230, 0, nil, nil, nil, nil, nil],
-
0xfe23 => [230, 0, nil, nil, nil, nil, nil],
-
0xfe30 => [0, 0, nil, "\342\200\245", nil, nil, nil],
-
0xfe31 => [0, 0, nil, "\342\200\224", nil, nil, nil],
-
0xfe32 => [0, 0, nil, "\342\200\223", nil, nil, nil],
-
0xfe33 => [0, 0, nil, "_", nil, nil, nil],
-
0xfe34 => [0, 0, nil, "_", nil, nil, nil],
-
0xfe35 => [0, 0, nil, "(", nil, nil, nil],
-
0xfe36 => [0, 0, nil, ")", nil, nil, nil],
-
0xfe37 => [0, 0, nil, "{", nil, nil, nil],
-
0xfe38 => [0, 0, nil, "}", nil, nil, nil],
-
0xfe39 => [0, 0, nil, "\343\200\224", nil, nil, nil],
-
0xfe3a => [0, 0, nil, "\343\200\225", nil, nil, nil],
-
0xfe3b => [0, 0, nil, "\343\200\220", nil, nil, nil],
-
0xfe3c => [0, 0, nil, "\343\200\221", nil, nil, nil],
-
0xfe3d => [0, 0, nil, "\343\200\212", nil, nil, nil],
-
0xfe3e => [0, 0, nil, "\343\200\213", nil, nil, nil],
-
0xfe3f => [0, 0, nil, "\343\200\210", nil, nil, nil],
-
0xfe40 => [0, 0, nil, "\343\200\211", nil, nil, nil],
-
0xfe41 => [0, 0, nil, "\343\200\214", nil, nil, nil],
-
0xfe42 => [0, 0, nil, "\343\200\215", nil, nil, nil],
-
0xfe43 => [0, 0, nil, "\343\200\216", nil, nil, nil],
-
0xfe44 => [0, 0, nil, "\343\200\217", nil, nil, nil],
-
0xfe49 => [0, 0, nil, "\342\200\276", nil, nil, nil],
-
0xfe4a => [0, 0, nil, "\342\200\276", nil, nil, nil],
-
0xfe4b => [0, 0, nil, "\342\200\276", nil, nil, nil],
-
0xfe4c => [0, 0, nil, "\342\200\276", nil, nil, nil],
-
0xfe4d => [0, 0, nil, "_", nil, nil, nil],
-
0xfe4e => [0, 0, nil, "_", nil, nil, nil],
-
0xfe4f => [0, 0, nil, "_", nil, nil, nil],
-
0xfe50 => [0, 0, nil, ",", nil, nil, nil],
-
0xfe51 => [0, 0, nil, "\343\200\201", nil, nil, nil],
-
0xfe52 => [0, 0, nil, ".", nil, nil, nil],
-
0xfe54 => [0, 0, nil, ";", nil, nil, nil],
-
0xfe55 => [0, 0, nil, ":", nil, nil, nil],
-
0xfe56 => [0, 0, nil, "?", nil, nil, nil],
-
0xfe57 => [0, 0, nil, "!", nil, nil, nil],
-
0xfe58 => [0, 0, nil, "\342\200\224", nil, nil, nil],
-
0xfe59 => [0, 0, nil, "(", nil, nil, nil],
-
0xfe5a => [0, 0, nil, ")", nil, nil, nil],
-
0xfe5b => [0, 0, nil, "{", nil, nil, nil],
-
0xfe5c => [0, 0, nil, "}", nil, nil, nil],
-
0xfe5d => [0, 0, nil, "\343\200\224", nil, nil, nil],
-
0xfe5e => [0, 0, nil, "\343\200\225", nil, nil, nil],
-
0xfe5f => [0, 0, nil, "#", nil, nil, nil],
-
0xfe60 => [0, 0, nil, "&", nil, nil, nil],
-
0xfe61 => [0, 0, nil, "*", nil, nil, nil],
-
0xfe62 => [0, 0, nil, "+", nil, nil, nil],
-
0xfe63 => [0, 0, nil, "-", nil, nil, nil],
-
0xfe64 => [0, 0, nil, "<", nil, nil, nil],
-
0xfe65 => [0, 0, nil, ">", nil, nil, nil],
-
0xfe66 => [0, 0, nil, "=", nil, nil, nil],
-
0xfe68 => [0, 0, nil, "\134", nil, nil, nil],
-
0xfe69 => [0, 0, nil, "$", nil, nil, nil],
-
0xfe6a => [0, 0, nil, "%", nil, nil, nil],
-
0xfe6b => [0, 0, nil, "@", nil, nil, nil],
-
0xfe70 => [0, 0, nil, " \331\213", nil, nil, nil],
-
0xfe71 => [0, 0, nil, "\331\200\331\213", nil, nil, nil],
-
0xfe72 => [0, 0, nil, " \331\214", nil, nil, nil],
-
0xfe74 => [0, 0, nil, " \331\215", nil, nil, nil],
-
0xfe76 => [0, 0, nil, " \331\216", nil, nil, nil],
-
0xfe77 => [0, 0, nil, "\331\200\331\216", nil, nil, nil],
-
0xfe78 => [0, 0, nil, " \331\217", nil, nil, nil],
-
0xfe79 => [0, 0, nil, "\331\200\331\217", nil, nil, nil],
-
0xfe7a => [0, 0, nil, " \331\220", nil, nil, nil],
-
0xfe7b => [0, 0, nil, "\331\200\331\220", nil, nil, nil],
-
0xfe7c => [0, 0, nil, " \331\221", nil, nil, nil],
-
0xfe7d => [0, 0, nil, "\331\200\331\221", nil, nil, nil],
-
0xfe7e => [0, 0, nil, " \331\222", nil, nil, nil],
-
0xfe7f => [0, 0, nil, "\331\200\331\222", nil, nil, nil],
-
0xfe80 => [0, 0, nil, "\330\241", nil, nil, nil],
-
0xfe81 => [0, 0, nil, "\330\242", nil, nil, nil],
-
0xfe82 => [0, 0, nil, "\330\242", nil, nil, nil],
-
0xfe83 => [0, 0, nil, "\330\243", nil, nil, nil],
-
0xfe84 => [0, 0, nil, "\330\243", nil, nil, nil],
-
0xfe85 => [0, 0, nil, "\330\244", nil, nil, nil],
-
0xfe86 => [0, 0, nil, "\330\244", nil, nil, nil],
-
0xfe87 => [0, 0, nil, "\330\245", nil, nil, nil],
-
0xfe88 => [0, 0, nil, "\330\245", nil, nil, nil],
-
0xfe89 => [0, 0, nil, "\330\246", nil, nil, nil],
-
0xfe8a => [0, 0, nil, "\330\246", nil, nil, nil],
-
0xfe8b => [0, 0, nil, "\330\246", nil, nil, nil],
-
0xfe8c => [0, 0, nil, "\330\246", nil, nil, nil],
-
0xfe8d => [0, 0, nil, "\330\247", nil, nil, nil],
-
0xfe8e => [0, 0, nil, "\330\247", nil, nil, nil],
-
0xfe8f => [0, 0, nil, "\330\250", nil, nil, nil],
-
0xfe90 => [0, 0, nil, "\330\250", nil, nil, nil],
-
0xfe91 => [0, 0, nil, "\330\250", nil, nil, nil],
-
0xfe92 => [0, 0, nil, "\330\250", nil, nil, nil],
-
0xfe93 => [0, 0, nil, "\330\251", nil, nil, nil],
-
0xfe94 => [0, 0, nil, "\330\251", nil, nil, nil],
-
0xfe95 => [0, 0, nil, "\330\252", nil, nil, nil],
-
0xfe96 => [0, 0, nil, "\330\252", nil, nil, nil],
-
0xfe97 => [0, 0, nil, "\330\252", nil, nil, nil],
-
0xfe98 => [0, 0, nil, "\330\252", nil, nil, nil],
-
0xfe99 => [0, 0, nil, "\330\253", nil, nil, nil],
-
0xfe9a => [0, 0, nil, "\330\253", nil, nil, nil],
-
0xfe9b => [0, 0, nil, "\330\253", nil, nil, nil],
-
0xfe9c => [0, 0, nil, "\330\253", nil, nil, nil],
-
0xfe9d => [0, 0, nil, "\330\254", nil, nil, nil],
-
0xfe9e => [0, 0, nil, "\330\254", nil, nil, nil],
-
0xfe9f => [0, 0, nil, "\330\254", nil, nil, nil],
-
0xfea0 => [0, 0, nil, "\330\254", nil, nil, nil],
-
0xfea1 => [0, 0, nil, "\330\255", nil, nil, nil],
-
0xfea2 => [0, 0, nil, "\330\255", nil, nil, nil],
-
0xfea3 => [0, 0, nil, "\330\255", nil, nil, nil],
-
0xfea4 => [0, 0, nil, "\330\255", nil, nil, nil],
-
0xfea5 => [0, 0, nil, "\330\256", nil, nil, nil],
-
0xfea6 => [0, 0, nil, "\330\256", nil, nil, nil],
-
0xfea7 => [0, 0, nil, "\330\256", nil, nil, nil],
-
0xfea8 => [0, 0, nil, "\330\256", nil, nil, nil],
-
0xfea9 => [0, 0, nil, "\330\257", nil, nil, nil],
-
0xfeaa => [0, 0, nil, "\330\257", nil, nil, nil],
-
0xfeab => [0, 0, nil, "\330\260", nil, nil, nil],
-
0xfeac => [0, 0, nil, "\330\260", nil, nil, nil],
-
0xfead => [0, 0, nil, "\330\261", nil, nil, nil],
-
0xfeae => [0, 0, nil, "\330\261", nil, nil, nil],
-
0xfeaf => [0, 0, nil, "\330\262", nil, nil, nil],
-
0xfeb0 => [0, 0, nil, "\330\262", nil, nil, nil],
-
0xfeb1 => [0, 0, nil, "\330\263", nil, nil, nil],
-
0xfeb2 => [0, 0, nil, "\330\263", nil, nil, nil],
-
0xfeb3 => [0, 0, nil, "\330\263", nil, nil, nil],
-
0xfeb4 => [0, 0, nil, "\330\263", nil, nil, nil],
-
0xfeb5 => [0, 0, nil, "\330\264", nil, nil, nil],
-
0xfeb6 => [0, 0, nil, "\330\264", nil, nil, nil],
-
0xfeb7 => [0, 0, nil, "\330\264", nil, nil, nil],
-
0xfeb8 => [0, 0, nil, "\330\264", nil, nil, nil],
-
0xfeb9 => [0, 0, nil, "\330\265", nil, nil, nil],
-
0xfeba => [0, 0, nil, "\330\265", nil, nil, nil],
-
0xfebb => [0, 0, nil, "\330\265", nil, nil, nil],
-
0xfebc => [0, 0, nil, "\330\265", nil, nil, nil],
-
0xfebd => [0, 0, nil, "\330\266", nil, nil, nil],
-
0xfebe => [0, 0, nil, "\330\266", nil, nil, nil],
-
0xfebf => [0, 0, nil, "\330\266", nil, nil, nil],
-
0xfec0 => [0, 0, nil, "\330\266", nil, nil, nil],
-
0xfec1 => [0, 0, nil, "\330\267", nil, nil, nil],
-
0xfec2 => [0, 0, nil, "\330\267", nil, nil, nil],
-
0xfec3 => [0, 0, nil, "\330\267", nil, nil, nil],
-
0xfec4 => [0, 0, nil, "\330\267", nil, nil, nil],
-
0xfec5 => [0, 0, nil, "\330\270", nil, nil, nil],
-
0xfec6 => [0, 0, nil, "\330\270", nil, nil, nil],
-
0xfec7 => [0, 0, nil, "\330\270", nil, nil, nil],
-
0xfec8 => [0, 0, nil, "\330\270", nil, nil, nil],
-
0xfec9 => [0, 0, nil, "\330\271", nil, nil, nil],
-
0xfeca => [0, 0, nil, "\330\271", nil, nil, nil],
-
0xfecb => [0, 0, nil, "\330\271", nil, nil, nil],
-
0xfecc => [0, 0, nil, "\330\271", nil, nil, nil],
-
0xfecd => [0, 0, nil, "\330\272", nil, nil, nil],
-
0xfece => [0, 0, nil, "\330\272", nil, nil, nil],
-
0xfecf => [0, 0, nil, "\330\272", nil, nil, nil],
-
0xfed0 => [0, 0, nil, "\330\272", nil, nil, nil],
-
0xfed1 => [0, 0, nil, "\331\201", nil, nil, nil],
-
0xfed2 => [0, 0, nil, "\331\201", nil, nil, nil],
-
0xfed3 => [0, 0, nil, "\331\201", nil, nil, nil],
-
0xfed4 => [0, 0, nil, "\331\201", nil, nil, nil],
-
0xfed5 => [0, 0, nil, "\331\202", nil, nil, nil],
-
0xfed6 => [0, 0, nil, "\331\202", nil, nil, nil],
-
0xfed7 => [0, 0, nil, "\331\202", nil, nil, nil],
-
0xfed8 => [0, 0, nil, "\331\202", nil, nil, nil],
-
0xfed9 => [0, 0, nil, "\331\203", nil, nil, nil],
-
0xfeda => [0, 0, nil, "\331\203", nil, nil, nil],
-
0xfedb => [0, 0, nil, "\331\203", nil, nil, nil],
-
0xfedc => [0, 0, nil, "\331\203", nil, nil, nil],
-
0xfedd => [0, 0, nil, "\331\204", nil, nil, nil],
-
0xfede => [0, 0, nil, "\331\204", nil, nil, nil],
-
0xfedf => [0, 0, nil, "\331\204", nil, nil, nil],
-
0xfee0 => [0, 0, nil, "\331\204", nil, nil, nil],
-
0xfee1 => [0, 0, nil, "\331\205", nil, nil, nil],
-
0xfee2 => [0, 0, nil, "\331\205", nil, nil, nil],
-
0xfee3 => [0, 0, nil, "\331\205", nil, nil, nil],
-
0xfee4 => [0, 0, nil, "\331\205", nil, nil, nil],
-
0xfee5 => [0, 0, nil, "\331\206", nil, nil, nil],
-
0xfee6 => [0, 0, nil, "\331\206", nil, nil, nil],
-
0xfee7 => [0, 0, nil, "\331\206", nil, nil, nil],
-
0xfee8 => [0, 0, nil, "\331\206", nil, nil, nil],
-
0xfee9 => [0, 0, nil, "\331\207", nil, nil, nil],
-
0xfeea => [0, 0, nil, "\331\207", nil, nil, nil],
-
0xfeeb => [0, 0, nil, "\331\207", nil, nil, nil],
-
0xfeec => [0, 0, nil, "\331\207", nil, nil, nil],
-
0xfeed => [0, 0, nil, "\331\210", nil, nil, nil],
-
0xfeee => [0, 0, nil, "\331\210", nil, nil, nil],
-
0xfeef => [0, 0, nil, "\331\211", nil, nil, nil],
-
0xfef0 => [0, 0, nil, "\331\211", nil, nil, nil],
-
0xfef1 => [0, 0, nil, "\331\212", nil, nil, nil],
-
0xfef2 => [0, 0, nil, "\331\212", nil, nil, nil],
-
0xfef3 => [0, 0, nil, "\331\212", nil, nil, nil],
-
0xfef4 => [0, 0, nil, "\331\212", nil, nil, nil],
-
0xfef5 => [0, 0, nil, "\331\204\330\242", nil, nil, nil],
-
0xfef6 => [0, 0, nil, "\331\204\330\242", nil, nil, nil],
-
0xfef7 => [0, 0, nil, "\331\204\330\243", nil, nil, nil],
-
0xfef8 => [0, 0, nil, "\331\204\330\243", nil, nil, nil],
-
0xfef9 => [0, 0, nil, "\331\204\330\245", nil, nil, nil],
-
0xfefa => [0, 0, nil, "\331\204\330\245", nil, nil, nil],
-
0xfefb => [0, 0, nil, "\331\204\330\247", nil, nil, nil],
-
0xfefc => [0, 0, nil, "\331\204\330\247", nil, nil, nil],
-
0xff01 => [0, 0, nil, "!", nil, nil, nil],
-
0xff02 => [0, 0, nil, "\042", nil, nil, nil],
-
0xff03 => [0, 0, nil, "#", nil, nil, nil],
-
0xff04 => [0, 0, nil, "$", nil, nil, nil],
-
0xff05 => [0, 0, nil, "%", nil, nil, nil],
-
0xff06 => [0, 0, nil, "&", nil, nil, nil],
-
0xff07 => [0, 0, nil, "'", nil, nil, nil],
-
0xff08 => [0, 0, nil, "(", nil, nil, nil],
-
0xff09 => [0, 0, nil, ")", nil, nil, nil],
-
0xff0a => [0, 0, nil, "*", nil, nil, nil],
-
0xff0b => [0, 0, nil, "+", nil, nil, nil],
-
0xff0c => [0, 0, nil, ",", nil, nil, nil],
-
0xff0d => [0, 0, nil, "-", nil, nil, nil],
-
0xff0e => [0, 0, nil, ".", nil, nil, nil],
-
0xff0f => [0, 0, nil, "/", nil, nil, nil],
-
0xff10 => [0, 0, nil, "0", nil, nil, nil],
-
0xff11 => [0, 0, nil, "1", nil, nil, nil],
-
0xff12 => [0, 0, nil, "2", nil, nil, nil],
-
0xff13 => [0, 0, nil, "3", nil, nil, nil],
-
0xff14 => [0, 0, nil, "4", nil, nil, nil],
-
0xff15 => [0, 0, nil, "5", nil, nil, nil],
-
0xff16 => [0, 0, nil, "6", nil, nil, nil],
-
0xff17 => [0, 0, nil, "7", nil, nil, nil],
-
0xff18 => [0, 0, nil, "8", nil, nil, nil],
-
0xff19 => [0, 0, nil, "9", nil, nil, nil],
-
0xff1a => [0, 0, nil, ":", nil, nil, nil],
-
0xff1b => [0, 0, nil, ";", nil, nil, nil],
-
0xff1c => [0, 0, nil, "<", nil, nil, nil],
-
0xff1d => [0, 0, nil, "=", nil, nil, nil],
-
0xff1e => [0, 0, nil, ">", nil, nil, nil],
-
0xff1f => [0, 0, nil, "?", nil, nil, nil],
-
0xff20 => [0, 0, nil, "@", nil, nil, nil],
-
0xff21 => [0, 0, nil, "A", nil, 0xff41, nil],
-
0xff22 => [0, 0, nil, "B", nil, 0xff42, nil],
-
0xff23 => [0, 0, nil, "C", nil, 0xff43, nil],
-
0xff24 => [0, 0, nil, "D", nil, 0xff44, nil],
-
0xff25 => [0, 0, nil, "E", nil, 0xff45, nil],
-
0xff26 => [0, 0, nil, "F", nil, 0xff46, nil],
-
0xff27 => [0, 0, nil, "G", nil, 0xff47, nil],
-
0xff28 => [0, 0, nil, "H", nil, 0xff48, nil],
-
0xff29 => [0, 0, nil, "I", nil, 0xff49, nil],
-
0xff2a => [0, 0, nil, "J", nil, 0xff4a, nil],
-
0xff2b => [0, 0, nil, "K", nil, 0xff4b, nil],
-
0xff2c => [0, 0, nil, "L", nil, 0xff4c, nil],
-
0xff2d => [0, 0, nil, "M", nil, 0xff4d, nil],
-
0xff2e => [0, 0, nil, "N", nil, 0xff4e, nil],
-
0xff2f => [0, 0, nil, "O", nil, 0xff4f, nil],
-
0xff30 => [0, 0, nil, "P", nil, 0xff50, nil],
-
0xff31 => [0, 0, nil, "Q", nil, 0xff51, nil],
-
0xff32 => [0, 0, nil, "R", nil, 0xff52, nil],
-
0xff33 => [0, 0, nil, "S", nil, 0xff53, nil],
-
0xff34 => [0, 0, nil, "T", nil, 0xff54, nil],
-
0xff35 => [0, 0, nil, "U", nil, 0xff55, nil],
-
0xff36 => [0, 0, nil, "V", nil, 0xff56, nil],
-
0xff37 => [0, 0, nil, "W", nil, 0xff57, nil],
-
0xff38 => [0, 0, nil, "X", nil, 0xff58, nil],
-
0xff39 => [0, 0, nil, "Y", nil, 0xff59, nil],
-
0xff3a => [0, 0, nil, "Z", nil, 0xff5a, nil],
-
0xff3b => [0, 0, nil, "[", nil, nil, nil],
-
0xff3c => [0, 0, nil, "\134", nil, nil, nil],
-
0xff3d => [0, 0, nil, "]", nil, nil, nil],
-
0xff3e => [0, 0, nil, "^", nil, nil, nil],
-
0xff3f => [0, 0, nil, "_", nil, nil, nil],
-
0xff40 => [0, 0, nil, "`", nil, nil, nil],
-
0xff41 => [0, 0, nil, "a", 0xff21, nil, 0xff21],
-
0xff42 => [0, 0, nil, "b", 0xff22, nil, 0xff22],
-
0xff43 => [0, 0, nil, "c", 0xff23, nil, 0xff23],
-
0xff44 => [0, 0, nil, "d", 0xff24, nil, 0xff24],
-
0xff45 => [0, 0, nil, "e", 0xff25, nil, 0xff25],
-
0xff46 => [0, 0, nil, "f", 0xff26, nil, 0xff26],
-
0xff47 => [0, 0, nil, "g", 0xff27, nil, 0xff27],
-
0xff48 => [0, 0, nil, "h", 0xff28, nil, 0xff28],
-
0xff49 => [0, 0, nil, "i", 0xff29, nil, 0xff29],
-
0xff4a => [0, 0, nil, "j", 0xff2a, nil, 0xff2a],
-
0xff4b => [0, 0, nil, "k", 0xff2b, nil, 0xff2b],
-
0xff4c => [0, 0, nil, "l", 0xff2c, nil, 0xff2c],
-
0xff4d => [0, 0, nil, "m", 0xff2d, nil, 0xff2d],
-
0xff4e => [0, 0, nil, "n", 0xff2e, nil, 0xff2e],
-
0xff4f => [0, 0, nil, "o", 0xff2f, nil, 0xff2f],
-
0xff50 => [0, 0, nil, "p", 0xff30, nil, 0xff30],
-
0xff51 => [0, 0, nil, "q", 0xff31, nil, 0xff31],
-
0xff52 => [0, 0, nil, "r", 0xff32, nil, 0xff32],
-
0xff53 => [0, 0, nil, "s", 0xff33, nil, 0xff33],
-
0xff54 => [0, 0, nil, "t", 0xff34, nil, 0xff34],
-
0xff55 => [0, 0, nil, "u", 0xff35, nil, 0xff35],
-
0xff56 => [0, 0, nil, "v", 0xff36, nil, 0xff36],
-
0xff57 => [0, 0, nil, "w", 0xff37, nil, 0xff37],
-
0xff58 => [0, 0, nil, "x", 0xff38, nil, 0xff38],
-
0xff59 => [0, 0, nil, "y", 0xff39, nil, 0xff39],
-
0xff5a => [0, 0, nil, "z", 0xff3a, nil, 0xff3a],
-
0xff5b => [0, 0, nil, "{", nil, nil, nil],
-
0xff5c => [0, 0, nil, "|", nil, nil, nil],
-
0xff5d => [0, 0, nil, "}", nil, nil, nil],
-
0xff5e => [0, 0, nil, "~", nil, nil, nil],
-
0xff61 => [0, 0, nil, "\343\200\202", nil, nil, nil],
-
0xff62 => [0, 0, nil, "\343\200\214", nil, nil, nil],
-
0xff63 => [0, 0, nil, "\343\200\215", nil, nil, nil],
-
0xff64 => [0, 0, nil, "\343\200\201", nil, nil, nil],
-
0xff65 => [0, 0, nil, "\343\203\273", nil, nil, nil],
-
0xff66 => [0, 0, nil, "\343\203\262", nil, nil, nil],
-
0xff67 => [0, 0, nil, "\343\202\241", nil, nil, nil],
-
0xff68 => [0, 0, nil, "\343\202\243", nil, nil, nil],
-
0xff69 => [0, 0, nil, "\343\202\245", nil, nil, nil],
-
0xff6a => [0, 0, nil, "\343\202\247", nil, nil, nil],
-
0xff6b => [0, 0, nil, "\343\202\251", nil, nil, nil],
-
0xff6c => [0, 0, nil, "\343\203\243", nil, nil, nil],
-
0xff6d => [0, 0, nil, "\343\203\245", nil, nil, nil],
-
0xff6e => [0, 0, nil, "\343\203\247", nil, nil, nil],
-
0xff6f => [0, 0, nil, "\343\203\203", nil, nil, nil],
-
0xff70 => [0, 0, nil, "\343\203\274", nil, nil, nil],
-
0xff71 => [0, 0, nil, "\343\202\242", nil, nil, nil],
-
0xff72 => [0, 0, nil, "\343\202\244", nil, nil, nil],
-
0xff73 => [0, 0, nil, "\343\202\246", nil, nil, nil],
-
0xff74 => [0, 0, nil, "\343\202\250", nil, nil, nil],
-
0xff75 => [0, 0, nil, "\343\202\252", nil, nil, nil],
-
0xff76 => [0, 0, nil, "\343\202\253", nil, nil, nil],
-
0xff77 => [0, 0, nil, "\343\202\255", nil, nil, nil],
-
0xff78 => [0, 0, nil, "\343\202\257", nil, nil, nil],
-
0xff79 => [0, 0, nil, "\343\202\261", nil, nil, nil],
-
0xff7a => [0, 0, nil, "\343\202\263", nil, nil, nil],
-
0xff7b => [0, 0, nil, "\343\202\265", nil, nil, nil],
-
0xff7c => [0, 0, nil, "\343\202\267", nil, nil, nil],
-
0xff7d => [0, 0, nil, "\343\202\271", nil, nil, nil],
-
0xff7e => [0, 0, nil, "\343\202\273", nil, nil, nil],
-
0xff7f => [0, 0, nil, "\343\202\275", nil, nil, nil],
-
0xff80 => [0, 0, nil, "\343\202\277", nil, nil, nil],
-
0xff81 => [0, 0, nil, "\343\203\201", nil, nil, nil],
-
0xff82 => [0, 0, nil, "\343\203\204", nil, nil, nil],
-
0xff83 => [0, 0, nil, "\343\203\206", nil, nil, nil],
-
0xff84 => [0, 0, nil, "\343\203\210", nil, nil, nil],
-
0xff85 => [0, 0, nil, "\343\203\212", nil, nil, nil],
-
0xff86 => [0, 0, nil, "\343\203\213", nil, nil, nil],
-
0xff87 => [0, 0, nil, "\343\203\214", nil, nil, nil],
-
0xff88 => [0, 0, nil, "\343\203\215", nil, nil, nil],
-
0xff89 => [0, 0, nil, "\343\203\216", nil, nil, nil],
-
0xff8a => [0, 0, nil, "\343\203\217", nil, nil, nil],
-
0xff8b => [0, 0, nil, "\343\203\222", nil, nil, nil],
-
0xff8c => [0, 0, nil, "\343\203\225", nil, nil, nil],
-
0xff8d => [0, 0, nil, "\343\203\230", nil, nil, nil],
-
0xff8e => [0, 0, nil, "\343\203\233", nil, nil, nil],
-
0xff8f => [0, 0, nil, "\343\203\236", nil, nil, nil],
-
0xff90 => [0, 0, nil, "\343\203\237", nil, nil, nil],
-
0xff91 => [0, 0, nil, "\343\203\240", nil, nil, nil],
-
0xff92 => [0, 0, nil, "\343\203\241", nil, nil, nil],
-
0xff93 => [0, 0, nil, "\343\203\242", nil, nil, nil],
-
0xff94 => [0, 0, nil, "\343\203\244", nil, nil, nil],
-
0xff95 => [0, 0, nil, "\343\203\246", nil, nil, nil],
-
0xff96 => [0, 0, nil, "\343\203\250", nil, nil, nil],
-
0xff97 => [0, 0, nil, "\343\203\251", nil, nil, nil],
-
0xff98 => [0, 0, nil, "\343\203\252", nil, nil, nil],
-
0xff99 => [0, 0, nil, "\343\203\253", nil, nil, nil],
-
0xff9a => [0, 0, nil, "\343\203\254", nil, nil, nil],
-
0xff9b => [0, 0, nil, "\343\203\255", nil, nil, nil],
-
0xff9c => [0, 0, nil, "\343\203\257", nil, nil, nil],
-
0xff9d => [0, 0, nil, "\343\203\263", nil, nil, nil],
-
0xff9e => [0, 0, nil, "\343\202\231", nil, nil, nil],
-
0xff9f => [0, 0, nil, "\343\202\232", nil, nil, nil],
-
0xffa0 => [0, 0, nil, "\343\205\244", nil, nil, nil],
-
0xffa1 => [0, 0, nil, "\343\204\261", nil, nil, nil],
-
0xffa2 => [0, 0, nil, "\343\204\262", nil, nil, nil],
-
0xffa3 => [0, 0, nil, "\343\204\263", nil, nil, nil],
-
0xffa4 => [0, 0, nil, "\343\204\264", nil, nil, nil],
-
0xffa5 => [0, 0, nil, "\343\204\265", nil, nil, nil],
-
0xffa6 => [0, 0, nil, "\343\204\266", nil, nil, nil],
-
0xffa7 => [0, 0, nil, "\343\204\267", nil, nil, nil],
-
0xffa8 => [0, 0, nil, "\343\204\270", nil, nil, nil],
-
0xffa9 => [0, 0, nil, "\343\204\271", nil, nil, nil],
-
0xffaa => [0, 0, nil, "\343\204\272", nil, nil, nil],
-
0xffab => [0, 0, nil, "\343\204\273", nil, nil, nil],
-
0xffac => [0, 0, nil, "\343\204\274", nil, nil, nil],
-
0xffad => [0, 0, nil, "\343\204\275", nil, nil, nil],
-
0xffae => [0, 0, nil, "\343\204\276", nil, nil, nil],
-
0xffaf => [0, 0, nil, "\343\204\277", nil, nil, nil],
-
0xffb0 => [0, 0, nil, "\343\205\200", nil, nil, nil],
-
0xffb1 => [0, 0, nil, "\343\205\201", nil, nil, nil],
-
0xffb2 => [0, 0, nil, "\343\205\202", nil, nil, nil],
-
0xffb3 => [0, 0, nil, "\343\205\203", nil, nil, nil],
-
0xffb4 => [0, 0, nil, "\343\205\204", nil, nil, nil],
-
0xffb5 => [0, 0, nil, "\343\205\205", nil, nil, nil],
-
0xffb6 => [0, 0, nil, "\343\205\206", nil, nil, nil],
-
0xffb7 => [0, 0, nil, "\343\205\207", nil, nil, nil],
-
0xffb8 => [0, 0, nil, "\343\205\210", nil, nil, nil],
-
0xffb9 => [0, 0, nil, "\343\205\211", nil, nil, nil],
-
0xffba => [0, 0, nil, "\343\205\212", nil, nil, nil],
-
0xffbb => [0, 0, nil, "\343\205\213", nil, nil, nil],
-
0xffbc => [0, 0, nil, "\343\205\214", nil, nil, nil],
-
0xffbd => [0, 0, nil, "\343\205\215", nil, nil, nil],
-
0xffbe => [0, 0, nil, "\343\205\216", nil, nil, nil],
-
0xffc2 => [0, 0, nil, "\343\205\217", nil, nil, nil],
-
0xffc3 => [0, 0, nil, "\343\205\220", nil, nil, nil],
-
0xffc4 => [0, 0, nil, "\343\205\221", nil, nil, nil],
-
0xffc5 => [0, 0, nil, "\343\205\222", nil, nil, nil],
-
0xffc6 => [0, 0, nil, "\343\205\223", nil, nil, nil],
-
0xffc7 => [0, 0, nil, "\343\205\224", nil, nil, nil],
-
0xffca => [0, 0, nil, "\343\205\225", nil, nil, nil],
-
0xffcb => [0, 0, nil, "\343\205\226", nil, nil, nil],
-
0xffcc => [0, 0, nil, "\343\205\227", nil, nil, nil],
-
0xffcd => [0, 0, nil, "\343\205\230", nil, nil, nil],
-
0xffce => [0, 0, nil, "\343\205\231", nil, nil, nil],
-
0xffcf => [0, 0, nil, "\343\205\232", nil, nil, nil],
-
0xffd2 => [0, 0, nil, "\343\205\233", nil, nil, nil],
-
0xffd3 => [0, 0, nil, "\343\205\234", nil, nil, nil],
-
0xffd4 => [0, 0, nil, "\343\205\235", nil, nil, nil],
-
0xffd5 => [0, 0, nil, "\343\205\236", nil, nil, nil],
-
0xffd6 => [0, 0, nil, "\343\205\237", nil, nil, nil],
-
0xffd7 => [0, 0, nil, "\343\205\240", nil, nil, nil],
-
0xffda => [0, 0, nil, "\343\205\241", nil, nil, nil],
-
0xffdb => [0, 0, nil, "\343\205\242", nil, nil, nil],
-
0xffdc => [0, 0, nil, "\343\205\243", nil, nil, nil],
-
0xffe0 => [0, 0, nil, "\302\242", nil, nil, nil],
-
0xffe1 => [0, 0, nil, "\302\243", nil, nil, nil],
-
0xffe2 => [0, 0, nil, "\302\254", nil, nil, nil],
-
0xffe3 => [0, 0, nil, "\302\257", nil, nil, nil],
-
0xffe4 => [0, 0, nil, "\302\246", nil, nil, nil],
-
0xffe5 => [0, 0, nil, "\302\245", nil, nil, nil],
-
0xffe6 => [0, 0, nil, "\342\202\251", nil, nil, nil],
-
0xffe8 => [0, 0, nil, "\342\224\202", nil, nil, nil],
-
0xffe9 => [0, 0, nil, "\342\206\220", nil, nil, nil],
-
0xffea => [0, 0, nil, "\342\206\221", nil, nil, nil],
-
0xffeb => [0, 0, nil, "\342\206\222", nil, nil, nil],
-
0xffec => [0, 0, nil, "\342\206\223", nil, nil, nil],
-
0xffed => [0, 0, nil, "\342\226\240", nil, nil, nil],
-
0xffee => [0, 0, nil, "\342\227\213", nil, nil, nil]
-
}
-
-
1
COMPOSITION_TABLE = {}
-
1
for codepoint, data in UNICODE_DATA
-
4233
canonical = data[UNICODE_DATA_CANONICAL]
-
4233
exclusion = data[UNICODE_DATA_EXCLUSION]
-
-
4233
if canonical && exclusion == 0
-
918
COMPOSITION_TABLE[canonical.unpack("C*")] = codepoint
-
end
-
end
-
-
1
UNICODE_MAX_LENGTH = 256
-
1
ACE_MAX_LENGTH = 256
-
-
1
PUNYCODE_BASE = 36
-
1
PUNYCODE_TMIN = 1
-
1
PUNYCODE_TMAX = 26
-
1
PUNYCODE_SKEW = 38
-
1
PUNYCODE_DAMP = 700
-
1
PUNYCODE_INITIAL_BIAS = 72
-
1
PUNYCODE_INITIAL_N = 0x80
-
1
PUNYCODE_DELIMITER = 0x2D
-
-
1
PUNYCODE_MAXINT = 1 << 64
-
-
1
PUNYCODE_PRINT_ASCII =
-
"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" +
-
"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" +
-
" !\"\#$%&'()*+,-./" +
-
"0123456789:;<=>?" +
-
"@ABCDEFGHIJKLMNO" +
-
"PQRSTUVWXYZ[\\]^_" +
-
"`abcdefghijklmno" +
-
"pqrstuvwxyz{|}~\n"
-
-
# Input is invalid.
-
1
class PunycodeBadInput < StandardError; end
-
# Output would exceed the space provided.
-
1
class PunycodeBigOutput < StandardError; end
-
# Input needs wider integers to process.
-
1
class PunycodeOverflow < StandardError; end
-
-
1
def self.punycode_encode(unicode)
-
input = unicode.unpack("U*")
-
output = [0] * (ACE_MAX_LENGTH + 1)
-
input_length = input.size
-
output_length = [ACE_MAX_LENGTH]
-
-
# Initialize the state
-
n = PUNYCODE_INITIAL_N
-
delta = out = 0
-
max_out = output_length[0]
-
bias = PUNYCODE_INITIAL_BIAS
-
-
# Handle the basic code points:
-
input_length.times do |j|
-
if punycode_basic?(input[j])
-
if max_out - out < 2
-
raise PunycodeBigOutput,
-
"Output would exceed the space provided."
-
end
-
output[out] = input[j]
-
out += 1
-
end
-
end
-
-
h = b = out
-
-
# h is the number of code points that have been handled, b is the
-
# number of basic code points, and out is the number of characters
-
# that have been output.
-
-
if b > 0
-
output[out] = PUNYCODE_DELIMITER
-
out += 1
-
end
-
-
# Main encoding loop:
-
-
while h < input_length
-
# All non-basic code points < n have been
-
# handled already. Find the next larger one:
-
-
m = PUNYCODE_MAXINT
-
input_length.times do |j|
-
m = input[j] if (n...m) === input[j]
-
end
-
-
# Increase delta enough to advance the decoder's
-
# <n,i> state to <m,0>, but guard against overflow:
-
-
if m - n > (PUNYCODE_MAXINT - delta) / (h + 1)
-
raise PunycodeOverflow, "Input needs wider integers to process."
-
end
-
delta += (m - n) * (h + 1)
-
n = m
-
-
input_length.times do |j|
-
# Punycode does not need to check whether input[j] is basic:
-
if input[j] < n
-
delta += 1
-
if delta == 0
-
raise PunycodeOverflow,
-
"Input needs wider integers to process."
-
end
-
end
-
-
if input[j] == n
-
# Represent delta as a generalized variable-length integer:
-
-
q = delta; k = PUNYCODE_BASE
-
while true
-
if out >= max_out
-
raise PunycodeBigOutput,
-
"Output would exceed the space provided."
-
end
-
t = (
-
if k <= bias
-
PUNYCODE_TMIN
-
elsif k >= bias + PUNYCODE_TMAX
-
PUNYCODE_TMAX
-
else
-
k - bias
-
end
-
)
-
break if q < t
-
output[out] =
-
punycode_encode_digit(t + (q - t) % (PUNYCODE_BASE - t))
-
out += 1
-
q = (q - t) / (PUNYCODE_BASE - t)
-
k += PUNYCODE_BASE
-
end
-
-
output[out] = punycode_encode_digit(q)
-
out += 1
-
bias = punycode_adapt(delta, h + 1, h == b)
-
delta = 0
-
h += 1
-
end
-
end
-
-
delta += 1
-
n += 1
-
end
-
-
output_length[0] = out
-
-
outlen = out
-
outlen.times do |j|
-
c = output[j]
-
unless c >= 0 && c <= 127
-
raise Exception, "Invalid output char."
-
end
-
unless PUNYCODE_PRINT_ASCII[c]
-
raise PunycodeBadInput, "Input is invalid."
-
end
-
end
-
-
output[0..outlen].map { |x| x.chr }.join("").sub(/\0+\z/, "")
-
end
-
2
(class <<self; private :punycode_encode; end)
-
-
1
def self.punycode_decode(punycode)
-
input = []
-
output = []
-
-
if ACE_MAX_LENGTH * 2 < punycode.size
-
raise PunycodeBigOutput, "Output would exceed the space provided."
-
end
-
punycode.each_byte do |c|
-
unless c >= 0 && c <= 127
-
raise PunycodeBadInput, "Input is invalid."
-
end
-
input.push(c)
-
end
-
-
input_length = input.length
-
output_length = [UNICODE_MAX_LENGTH]
-
-
# Initialize the state
-
n = PUNYCODE_INITIAL_N
-
-
out = i = 0
-
max_out = output_length[0]
-
bias = PUNYCODE_INITIAL_BIAS
-
-
# Handle the basic code points: Let b be the number of input code
-
# points before the last delimiter, or 0 if there is none, then
-
# copy the first b code points to the output.
-
-
b = 0
-
input_length.times do |j|
-
b = j if punycode_delimiter?(input[j])
-
end
-
if b > max_out
-
raise PunycodeBigOutput, "Output would exceed the space provided."
-
end
-
-
b.times do |j|
-
unless punycode_basic?(input[j])
-
raise PunycodeBadInput, "Input is invalid."
-
end
-
output[out] = input[j]
-
out+=1
-
end
-
-
# Main decoding loop: Start just after the last delimiter if any
-
# basic code points were copied; start at the beginning otherwise.
-
-
in_ = b > 0 ? b + 1 : 0
-
while in_ < input_length
-
-
# in_ is the index of the next character to be consumed, and
-
# out is the number of code points in the output array.
-
-
# Decode a generalized variable-length integer into delta,
-
# which gets added to i. The overflow checking is easier
-
# if we increase i as we go, then subtract off its starting
-
# value at the end to obtain delta.
-
-
oldi = i; w = 1; k = PUNYCODE_BASE
-
while true
-
if in_ >= input_length
-
raise PunycodeBadInput, "Input is invalid."
-
end
-
digit = punycode_decode_digit(input[in_])
-
in_+=1
-
if digit >= PUNYCODE_BASE
-
raise PunycodeBadInput, "Input is invalid."
-
end
-
if digit > (PUNYCODE_MAXINT - i) / w
-
raise PunycodeOverflow, "Input needs wider integers to process."
-
end
-
i += digit * w
-
t = (
-
if k <= bias
-
PUNYCODE_TMIN
-
elsif k >= bias + PUNYCODE_TMAX
-
PUNYCODE_TMAX
-
else
-
k - bias
-
end
-
)
-
break if digit < t
-
if w > PUNYCODE_MAXINT / (PUNYCODE_BASE - t)
-
raise PunycodeOverflow, "Input needs wider integers to process."
-
end
-
w *= PUNYCODE_BASE - t
-
k += PUNYCODE_BASE
-
end
-
-
bias = punycode_adapt(i - oldi, out + 1, oldi == 0)
-
-
# I was supposed to wrap around from out + 1 to 0,
-
# incrementing n each time, so we'll fix that now:
-
-
if i / (out + 1) > PUNYCODE_MAXINT - n
-
raise PunycodeOverflow, "Input needs wider integers to process."
-
end
-
n += i / (out + 1)
-
i %= out + 1
-
-
# Insert n at position i of the output:
-
-
# not needed for Punycode:
-
# raise PUNYCODE_INVALID_INPUT if decode_digit(n) <= base
-
if out >= max_out
-
raise PunycodeBigOutput, "Output would exceed the space provided."
-
end
-
-
#memmove(output + i + 1, output + i, (out - i) * sizeof *output)
-
output[i + 1, out - i] = output[i, out - i]
-
output[i] = n
-
i += 1
-
-
out += 1
-
end
-
-
output_length[0] = out
-
-
output.pack("U*")
-
end
-
2
(class <<self; private :punycode_decode; end)
-
-
1
def self.punycode_basic?(codepoint)
-
codepoint < 0x80
-
end
-
2
(class <<self; private :punycode_basic?; end)
-
-
1
def self.punycode_delimiter?(codepoint)
-
codepoint == PUNYCODE_DELIMITER
-
end
-
2
(class <<self; private :punycode_delimiter?; end)
-
-
1
def self.punycode_encode_digit(d)
-
d + 22 + 75 * ((d < 26) ? 1 : 0)
-
end
-
2
(class <<self; private :punycode_encode_digit; end)
-
-
# Returns the numeric value of a basic codepoint
-
# (for use in representing integers) in the range 0 to
-
# base - 1, or PUNYCODE_BASE if codepoint does not represent a value.
-
1
def self.punycode_decode_digit(codepoint)
-
if codepoint - 48 < 10
-
codepoint - 22
-
elsif codepoint - 65 < 26
-
codepoint - 65
-
elsif codepoint - 97 < 26
-
codepoint - 97
-
else
-
PUNYCODE_BASE
-
end
-
end
-
2
(class <<self; private :punycode_decode_digit; end)
-
-
# Bias adaptation method
-
1
def self.punycode_adapt(delta, numpoints, firsttime)
-
delta = firsttime ? delta / PUNYCODE_DAMP : delta >> 1
-
# delta >> 1 is a faster way of doing delta / 2
-
delta += delta / numpoints
-
difference = PUNYCODE_BASE - PUNYCODE_TMIN
-
-
k = 0
-
while delta > (difference * PUNYCODE_TMAX) / 2
-
delta /= difference
-
k += PUNYCODE_BASE
-
end
-
-
k + (difference + 1) * delta / (delta + PUNYCODE_SKEW)
-
end
-
2
(class <<self; private :punycode_adapt; end)
-
end
-
# :startdoc:
-
end
-
# encoding:utf-8
-
#--
-
# Copyright (C) 2006-2011 Bob Aman
-
#
-
# Licensed under the Apache License, Version 2.0 (the "License");
-
# you may not use this file except in compliance with the License.
-
# You may obtain a copy of the License at
-
#
-
# http://www.apache.org/licenses/LICENSE-2.0
-
#
-
# Unless required by applicable law or agreed to in writing, software
-
# distributed under the License is distributed on an "AS IS" BASIS,
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-
# See the License for the specific language governing permissions and
-
# limitations under the License.
-
#++
-
-
-
1
require "addressable/version"
-
1
require "addressable/idna"
-
-
##
-
# Addressable is a library for processing links and URIs.
-
1
module Addressable
-
##
-
# This is an implementation of a URI parser based on
-
# <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>,
-
# <a href="http://www.ietf.org/rfc/rfc3987.txt">RFC 3987</a>.
-
1
class URI
-
##
-
# Raised if something other than a uri is supplied.
-
1
class InvalidURIError < StandardError
-
end
-
-
##
-
# Container for the character classes specified in
-
# <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
-
1
module CharacterClasses
-
1
ALPHA = "a-zA-Z"
-
1
DIGIT = "0-9"
-
1
GEN_DELIMS = "\\:\\/\\?\\#\\[\\]\\@"
-
1
SUB_DELIMS = "\\!\\$\\&\\'\\(\\)\\*\\+\\,\\;\\="
-
1
RESERVED = GEN_DELIMS + SUB_DELIMS
-
1
UNRESERVED = ALPHA + DIGIT + "\\-\\.\\_\\~"
-
1
PCHAR = UNRESERVED + SUB_DELIMS + "\\:\\@"
-
1
SCHEME = ALPHA + DIGIT + "\\-\\+\\."
-
1
AUTHORITY = PCHAR
-
1
PATH = PCHAR + "\\/"
-
1
QUERY = PCHAR + "\\/\\?"
-
1
FRAGMENT = PCHAR + "\\/\\?"
-
end
-
-
1
SLASH = '/'
-
1
EMPTYSTR = ''
-
-
1
URIREGEX = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/
-
-
1
PORT_MAPPING = {
-
"http" => 80,
-
"https" => 443,
-
"ftp" => 21,
-
"tftp" => 69,
-
"sftp" => 22,
-
"ssh" => 22,
-
"svn+ssh" => 22,
-
"telnet" => 23,
-
"nntp" => 119,
-
"gopher" => 70,
-
"wais" => 210,
-
"ldap" => 389,
-
"prospero" => 1525
-
}
-
-
##
-
# Returns a URI object based on the parsed string.
-
#
-
# @param [String, Addressable::URI, #to_str] uri
-
# The URI string to parse.
-
# No parsing is performed if the object is already an
-
# <code>Addressable::URI</code>.
-
#
-
# @return [Addressable::URI] The parsed URI.
-
1
def self.parse(uri)
-
# If we were given nil, return nil.
-
return nil unless uri
-
# If a URI object is passed, just return itself.
-
return uri.dup if uri.kind_of?(self)
-
-
# If a URI object of the Ruby standard library variety is passed,
-
# convert it to a string, then parse the string.
-
# We do the check this way because we don't want to accidentally
-
# cause a missing constant exception to be thrown.
-
if uri.class.name =~ /^URI\b/
-
uri = uri.to_s
-
end
-
-
# Otherwise, convert to a String
-
begin
-
uri = uri.to_str
-
rescue TypeError, NoMethodError
-
raise TypeError, "Can't convert #{uri.class} into String."
-
end if not uri.is_a? String
-
-
# This Regexp supplied as an example in RFC 3986, and it works great.
-
scan = uri.scan(URIREGEX)
-
fragments = scan[0]
-
scheme = fragments[1]
-
authority = fragments[3]
-
path = fragments[4]
-
query = fragments[6]
-
fragment = fragments[8]
-
user = nil
-
password = nil
-
host = nil
-
port = nil
-
if authority != nil
-
# The Regexp above doesn't split apart the authority.
-
userinfo = authority[/^([^\[\]]*)@/, 1]
-
if userinfo != nil
-
user = userinfo.strip[/^([^:]*):?/, 1]
-
password = userinfo.strip[/:(.*)$/, 1]
-
end
-
host = authority.gsub(/^([^\[\]]*)@/, EMPTYSTR).gsub(/:([^:@\[\]]*?)$/, EMPTYSTR)
-
port = authority[/:([^:@\[\]]*?)$/, 1]
-
end
-
if port == EMPTYSTR
-
port = nil
-
end
-
-
return new(
-
:scheme => scheme,
-
:user => user,
-
:password => password,
-
:host => host,
-
:port => port,
-
:path => path,
-
:query => query,
-
:fragment => fragment
-
)
-
end
-
-
##
-
# Converts an input to a URI. The input does not have to be a valid
-
# URI — the method will use heuristics to guess what URI was intended.
-
# This is not standards-compliant, merely user-friendly.
-
#
-
# @param [String, Addressable::URI, #to_str] uri
-
# The URI string to parse.
-
# No parsing is performed if the object is already an
-
# <code>Addressable::URI</code>.
-
# @param [Hash] hints
-
# A <code>Hash</code> of hints to the heuristic parser.
-
# Defaults to <code>{:scheme => "http"}</code>.
-
#
-
# @return [Addressable::URI] The parsed URI.
-
1
def self.heuristic_parse(uri, hints={})
-
# If we were given nil, return nil.
-
return nil unless uri
-
# If a URI object is passed, just return itself.
-
return uri.dup if uri.kind_of?(self)
-
if !uri.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{uri.class} into String."
-
end
-
# Otherwise, convert to a String
-
uri = uri.to_str.dup
-
hints = {
-
:scheme => "http"
-
}.merge(hints)
-
case uri
-
when /^http:\/+/
-
uri.gsub!(/^http:\/+/, "http://")
-
when /^https:\/+/
-
uri.gsub!(/^https:\/+/, "https://")
-
when /^feed:\/+http:\/+/
-
uri.gsub!(/^feed:\/+http:\/+/, "feed:http://")
-
when /^feed:\/+/
-
uri.gsub!(/^feed:\/+/, "feed://")
-
when /^file:\/+/
-
uri.gsub!(/^file:\/+/, "file:///")
-
end
-
parsed = self.parse(uri)
-
if parsed.scheme =~ /^[^\/?#\.]+\.[^\/?#]+$/
-
parsed = self.parse(hints[:scheme] + "://" + uri)
-
end
-
if parsed.path.include?(".")
-
new_host = parsed.path[/^([^\/]+\.[^\/]*)/, 1]
-
if new_host
-
parsed.defer_validation do
-
new_path = parsed.path.gsub(
-
Regexp.new("^" + Regexp.escape(new_host)), EMPTYSTR)
-
parsed.host = new_host
-
parsed.path = new_path
-
parsed.scheme = hints[:scheme] unless parsed.scheme
-
end
-
end
-
end
-
return parsed
-
end
-
-
##
-
# Converts a path to a file scheme URI. If the path supplied is
-
# relative, it will be returned as a relative URI. If the path supplied
-
# is actually a non-file URI, it will parse the URI as if it had been
-
# parsed with <code>Addressable::URI.parse</code>. Handles all of the
-
# various Microsoft-specific formats for specifying paths.
-
#
-
# @param [String, Addressable::URI, #to_str] path
-
# Typically a <code>String</code> path to a file or directory, but
-
# will return a sensible return value if an absolute URI is supplied
-
# instead.
-
#
-
# @return [Addressable::URI]
-
# The parsed file scheme URI or the original URI if some other URI
-
# scheme was provided.
-
#
-
# @example
-
# base = Addressable::URI.convert_path("/absolute/path/")
-
# uri = Addressable::URI.convert_path("relative/path")
-
# (base + uri).to_s
-
# #=> "file:///absolute/path/relative/path"
-
#
-
# Addressable::URI.convert_path(
-
# "c:\\windows\\My Documents 100%20\\foo.txt"
-
# ).to_s
-
# #=> "file:///c:/windows/My%20Documents%20100%20/foo.txt"
-
#
-
# Addressable::URI.convert_path("http://example.com/").to_s
-
# #=> "http://example.com/"
-
1
def self.convert_path(path)
-
# If we were given nil, return nil.
-
return nil unless path
-
# If a URI object is passed, just return itself.
-
return path if path.kind_of?(self)
-
if !path.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{path.class} into String."
-
end
-
# Otherwise, convert to a String
-
path = path.to_str.strip
-
-
path.gsub!(/^file:\/?\/?/, EMPTYSTR) if path =~ /^file:\/?\/?/
-
path = SLASH + path if path =~ /^([a-zA-Z])[\|:]/
-
uri = self.parse(path)
-
-
if uri.scheme == nil
-
# Adjust windows-style uris
-
uri.path.gsub!(/^\/?([a-zA-Z])[\|:][\\\/]/) do
-
"/#{$1.downcase}:/"
-
end
-
uri.path.gsub!(/\\/, SLASH)
-
if File.exists?(uri.path) &&
-
File.stat(uri.path).directory?
-
uri.path.gsub!(/\/$/, EMPTYSTR)
-
uri.path = uri.path + '/'
-
end
-
-
# If the path is absolute, set the scheme and host.
-
if uri.path =~ /^\//
-
uri.scheme = "file"
-
uri.host = EMPTYSTR
-
end
-
uri.normalize!
-
end
-
-
return uri
-
end
-
-
##
-
# Joins several URIs together.
-
#
-
# @param [String, Addressable::URI, #to_str] *uris
-
# The URIs to join.
-
#
-
# @return [Addressable::URI] The joined URI.
-
#
-
# @example
-
# base = "http://example.com/"
-
# uri = Addressable::URI.parse("relative/path")
-
# Addressable::URI.join(base, uri)
-
# #=> #<Addressable::URI:0xcab390 URI:http://example.com/relative/path>
-
1
def self.join(*uris)
-
uri_objects = uris.collect do |uri|
-
if !uri.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{uri.class} into String."
-
end
-
uri.kind_of?(self) ? uri : self.parse(uri.to_str)
-
end
-
result = uri_objects.shift.dup
-
for uri in uri_objects
-
result.join!(uri)
-
end
-
return result
-
end
-
-
##
-
# Percent encodes a URI component.
-
#
-
# @param [String, #to_str] component The URI component to encode.
-
#
-
# @param [String, Regexp] character_class
-
# The characters which are not percent encoded. If a <code>String</code>
-
# is passed, the <code>String</code> must be formatted as a regular
-
# expression character class. (Do not include the surrounding square
-
# brackets.) For example, <code>"b-zB-Z0-9"</code> would cause
-
# everything but the letters 'b' through 'z' and the numbers '0' through
-
# '9' to be percent encoded. If a <code>Regexp</code> is passed, the
-
# value <code>/[^b-zB-Z0-9]/</code> would have the same effect. A set of
-
# useful <code>String</code> values may be found in the
-
# <code>Addressable::URI::CharacterClasses</code> module. The default
-
# value is the reserved plus unreserved character classes specified in
-
# <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
-
#
-
# @return [String] The encoded component.
-
#
-
# @example
-
# Addressable::URI.encode_component("simple/example", "b-zB-Z0-9")
-
# => "simple%2Fex%61mple"
-
# Addressable::URI.encode_component("simple/example", /[^b-zB-Z0-9]/)
-
# => "simple%2Fex%61mple"
-
# Addressable::URI.encode_component(
-
# "simple/example", Addressable::URI::CharacterClasses::UNRESERVED
-
# )
-
# => "simple%2Fexample"
-
1
def self.encode_component(component, character_class=
-
CharacterClasses::RESERVED + CharacterClasses::UNRESERVED)
-
return nil if component.nil?
-
-
begin
-
if component.kind_of?(Symbol) || component.kind_of?(Numeric)
-
component = component.to_s
-
else
-
component = component.to_str
-
end
-
rescue TypeError, NoMethodError
-
raise TypeError, "Can't convert #{component.class} into String."
-
end if !component.is_a? String
-
-
if ![String, Regexp].include?(character_class.class)
-
raise TypeError,
-
"Expected String or Regexp, got #{character_class.inspect}"
-
end
-
if character_class.kind_of?(String)
-
character_class = /[^#{character_class}]/
-
end
-
if component.respond_to?(:force_encoding)
-
# We can't perform regexps on invalid UTF sequences, but
-
# here we need to, so switch to ASCII.
-
component = component.dup
-
component.force_encoding(Encoding::ASCII_8BIT)
-
end
-
return component.gsub(character_class) do |sequence|
-
(sequence.unpack('C*').map { |c| "%" + ("%02x" % c).upcase }).join
-
end
-
end
-
-
1
class << self
-
1
alias_method :encode_component, :encode_component
-
end
-
-
##
-
# Unencodes any percent encoded characters within a URI component.
-
# This method may be used for unencoding either components or full URIs,
-
# however, it is recommended to use the <code>unencode_component</code>
-
# alias when unencoding components.
-
#
-
# @param [String, Addressable::URI, #to_str] uri
-
# The URI or component to unencode.
-
#
-
# @param [Class] returning
-
# The type of object to return.
-
# This value may only be set to <code>String</code> or
-
# <code>Addressable::URI</code>. All other values are invalid. Defaults
-
# to <code>String</code>.
-
#
-
# @return [String, Addressable::URI]
-
# The unencoded component or URI.
-
# The return type is determined by the <code>returning</code> parameter.
-
1
def self.unencode(uri, returning=String)
-
return nil if uri.nil?
-
-
begin
-
uri = uri.to_str
-
rescue NoMethodError, TypeError
-
raise TypeError, "Can't convert #{uri.class} into String."
-
end if !uri.is_a? String
-
if ![String, ::Addressable::URI].include?(returning)
-
raise TypeError,
-
"Expected Class (String or Addressable::URI), " +
-
"got #{returning.inspect}"
-
end
-
result = uri.gsub(/%[0-9a-f]{2}/i) do |sequence|
-
sequence[1..3].to_i(16).chr
-
end
-
result.force_encoding("utf-8") if result.respond_to?(:force_encoding)
-
if returning == String
-
return result
-
elsif returning == ::Addressable::URI
-
return ::Addressable::URI.parse(result)
-
end
-
end
-
-
1
class << self
-
1
alias_method :unescape, :unencode
-
1
alias_method :unencode_component, :unencode
-
1
alias_method :unescape_component, :unencode
-
end
-
-
-
##
-
# Normalizes the encoding of a URI component.
-
#
-
# @param [String, #to_str] component The URI component to encode.
-
#
-
# @param [String, Regexp] character_class
-
# The characters which are not percent encoded. If a <code>String</code>
-
# is passed, the <code>String</code> must be formatted as a regular
-
# expression character class. (Do not include the surrounding square
-
# brackets.) For example, <code>"b-zB-Z0-9"</code> would cause
-
# everything but the letters 'b' through 'z' and the numbers '0'
-
# through '9' to be percent encoded. If a <code>Regexp</code> is passed,
-
# the value <code>/[^b-zB-Z0-9]/</code> would have the same effect. A
-
# set of useful <code>String</code> values may be found in the
-
# <code>Addressable::URI::CharacterClasses</code> module. The default
-
# value is the reserved plus unreserved character classes specified in
-
# <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
-
#
-
# @return [String] The normalized component.
-
#
-
# @example
-
# Addressable::URI.normalize_component("simpl%65/%65xampl%65", "b-zB-Z")
-
# => "simple%2Fex%61mple"
-
# Addressable::URI.normalize_component(
-
# "simpl%65/%65xampl%65", /[^b-zB-Z]/
-
# )
-
# => "simple%2Fex%61mple"
-
# Addressable::URI.normalize_component(
-
# "simpl%65/%65xampl%65",
-
# Addressable::URI::CharacterClasses::UNRESERVED
-
# )
-
# => "simple%2Fexample"
-
1
def self.normalize_component(component, character_class=
-
CharacterClasses::RESERVED + CharacterClasses::UNRESERVED)
-
return nil if component.nil?
-
-
begin
-
component = component.to_str
-
rescue NoMethodError, TypeError
-
raise TypeError, "Can't convert #{component.class} into String."
-
end if !component.is_a? String
-
-
if ![String, Regexp].include?(character_class.class)
-
raise TypeError,
-
"Expected String or Regexp, got #{character_class.inspect}"
-
end
-
if character_class.kind_of?(String)
-
character_class = /[^#{character_class}]/
-
end
-
if component.respond_to?(:force_encoding)
-
# We can't perform regexps on invalid UTF sequences, but
-
# here we need to, so switch to ASCII.
-
component = component.dup
-
component.force_encoding(Encoding::ASCII_8BIT)
-
end
-
unencoded = self.unencode_component(component)
-
begin
-
encoded = self.encode_component(
-
Addressable::IDNA.unicode_normalize_kc(unencoded),
-
character_class
-
)
-
rescue ArgumentError
-
encoded = self.encode_component(unencoded)
-
end
-
return encoded
-
end
-
-
##
-
# Percent encodes any special characters in the URI.
-
#
-
# @param [String, Addressable::URI, #to_str] uri
-
# The URI to encode.
-
#
-
# @param [Class] returning
-
# The type of object to return.
-
# This value may only be set to <code>String</code> or
-
# <code>Addressable::URI</code>. All other values are invalid. Defaults
-
# to <code>String</code>.
-
#
-
# @return [String, Addressable::URI]
-
# The encoded URI.
-
# The return type is determined by the <code>returning</code> parameter.
-
1
def self.encode(uri, returning=String)
-
return nil if uri.nil?
-
-
begin
-
uri = uri.to_str
-
rescue NoMethodError, TypeError
-
raise TypeError, "Can't convert #{uri.class} into String."
-
end if !uri.is_a? String
-
-
if ![String, ::Addressable::URI].include?(returning)
-
raise TypeError,
-
"Expected Class (String or Addressable::URI), " +
-
"got #{returning.inspect}"
-
end
-
uri_object = uri.kind_of?(self) ? uri : self.parse(uri)
-
encoded_uri = Addressable::URI.new(
-
:scheme => self.encode_component(uri_object.scheme,
-
Addressable::URI::CharacterClasses::SCHEME),
-
:authority => self.encode_component(uri_object.authority,
-
Addressable::URI::CharacterClasses::AUTHORITY),
-
:path => self.encode_component(uri_object.path,
-
Addressable::URI::CharacterClasses::PATH),
-
:query => self.encode_component(uri_object.query,
-
Addressable::URI::CharacterClasses::QUERY),
-
:fragment => self.encode_component(uri_object.fragment,
-
Addressable::URI::CharacterClasses::FRAGMENT)
-
)
-
if returning == String
-
return encoded_uri.to_s
-
elsif returning == ::Addressable::URI
-
return encoded_uri
-
end
-
end
-
-
1
class << self
-
1
alias_method :escape, :encode
-
end
-
-
##
-
# Normalizes the encoding of a URI. Characters within a hostname are
-
# not percent encoded to allow for internationalized domain names.
-
#
-
# @param [String, Addressable::URI, #to_str] uri
-
# The URI to encode.
-
#
-
# @param [Class] returning
-
# The type of object to return.
-
# This value may only be set to <code>String</code> or
-
# <code>Addressable::URI</code>. All other values are invalid. Defaults
-
# to <code>String</code>.
-
#
-
# @return [String, Addressable::URI]
-
# The encoded URI.
-
# The return type is determined by the <code>returning</code> parameter.
-
1
def self.normalized_encode(uri, returning=String)
-
begin
-
uri = uri.to_str
-
rescue NoMethodError, TypeError
-
raise TypeError, "Can't convert #{uri.class} into String."
-
end if !uri.is_a? String
-
-
if ![String, ::Addressable::URI].include?(returning)
-
raise TypeError,
-
"Expected Class (String or Addressable::URI), " +
-
"got #{returning.inspect}"
-
end
-
uri_object = uri.kind_of?(self) ? uri : self.parse(uri)
-
components = {
-
:scheme => self.unencode_component(uri_object.scheme),
-
:user => self.unencode_component(uri_object.user),
-
:password => self.unencode_component(uri_object.password),
-
:host => self.unencode_component(uri_object.host),
-
:port => uri_object.port,
-
:path => self.unencode_component(uri_object.path),
-
:query => self.unencode_component(uri_object.query),
-
:fragment => self.unencode_component(uri_object.fragment)
-
}
-
components.each do |key, value|
-
if value != nil
-
begin
-
components[key] =
-
Addressable::IDNA.unicode_normalize_kc(value.to_str)
-
rescue ArgumentError
-
# Likely a malformed UTF-8 character, skip unicode normalization
-
components[key] = value.to_str
-
end
-
end
-
end
-
encoded_uri = Addressable::URI.new(
-
:scheme => self.encode_component(components[:scheme],
-
Addressable::URI::CharacterClasses::SCHEME),
-
:user => self.encode_component(components[:user],
-
Addressable::URI::CharacterClasses::UNRESERVED),
-
:password => self.encode_component(components[:password],
-
Addressable::URI::CharacterClasses::UNRESERVED),
-
:host => components[:host],
-
:port => components[:port],
-
:path => self.encode_component(components[:path],
-
Addressable::URI::CharacterClasses::PATH),
-
:query => self.encode_component(components[:query],
-
Addressable::URI::CharacterClasses::QUERY),
-
:fragment => self.encode_component(components[:fragment],
-
Addressable::URI::CharacterClasses::FRAGMENT)
-
)
-
if returning == String
-
return encoded_uri.to_s
-
elsif returning == ::Addressable::URI
-
return encoded_uri
-
end
-
end
-
-
##
-
# Encodes a set of key/value pairs according to the rules for the
-
# <code>application/x-www-form-urlencoded</code> MIME type.
-
#
-
# @param [#to_hash, #to_ary] form_values
-
# The form values to encode.
-
#
-
# @param [TrueClass, FalseClass] sort
-
# Sort the key/value pairs prior to encoding.
-
# Defaults to <code>false</code>.
-
#
-
# @return [String]
-
# The encoded value.
-
1
def self.form_encode(form_values, sort=false)
-
if form_values.respond_to?(:to_hash)
-
form_values = form_values.to_hash.to_a
-
elsif form_values.respond_to?(:to_ary)
-
form_values = form_values.to_ary
-
else
-
raise TypeError, "Can't convert #{form_values.class} into Array."
-
end
-
form_values = form_values.map do |(key, value)|
-
[key.to_s, value.to_s]
-
end
-
if sort
-
# Useful for OAuth and optimizing caching systems
-
form_values = form_values.sort
-
end
-
escaped_form_values = form_values.map do |(key, value)|
-
# Line breaks are CRLF pairs
-
[
-
self.encode_component(
-
key.gsub(/(\r\n|\n|\r)/, "\r\n"),
-
CharacterClasses::UNRESERVED
-
).gsub("%20", "+"),
-
self.encode_component(
-
value.gsub(/(\r\n|\n|\r)/, "\r\n"),
-
CharacterClasses::UNRESERVED
-
).gsub("%20", "+")
-
]
-
end
-
return (escaped_form_values.map do |(key, value)|
-
"#{key}=#{value}"
-
end).join("&")
-
end
-
-
##
-
# Decodes a <code>String</code> according to the rules for the
-
# <code>application/x-www-form-urlencoded</code> MIME type.
-
#
-
# @param [String, #to_str] encoded_value
-
# The form values to decode.
-
#
-
# @return [Array]
-
# The decoded values.
-
# This is not a <code>Hash</code> because of the possibility for
-
# duplicate keys.
-
1
def self.form_unencode(encoded_value)
-
if !encoded_value.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{encoded_value.class} into String."
-
end
-
encoded_value = encoded_value.to_str
-
split_values = encoded_value.split("&").map do |pair|
-
pair.split("=", 2)
-
end
-
return split_values.map do |(key, value)|
-
[
-
key ? self.unencode_component(
-
key.gsub("+", "%20")).gsub(/(\r\n|\n|\r)/, "\n") : nil,
-
value ? (self.unencode_component(
-
value.gsub("+", "%20")).gsub(/(\r\n|\n|\r)/, "\n")) : nil
-
]
-
end
-
end
-
-
##
-
# Creates a new uri object from component parts.
-
#
-
# @option [String, #to_str] scheme The scheme component.
-
# @option [String, #to_str] user The user component.
-
# @option [String, #to_str] password The password component.
-
# @option [String, #to_str] userinfo
-
# The userinfo component. If this is supplied, the user and password
-
# components must be omitted.
-
# @option [String, #to_str] host The host component.
-
# @option [String, #to_str] port The port component.
-
# @option [String, #to_str] authority
-
# The authority component. If this is supplied, the user, password,
-
# userinfo, host, and port components must be omitted.
-
# @option [String, #to_str] path The path component.
-
# @option [String, #to_str] query The query component.
-
# @option [String, #to_str] fragment The fragment component.
-
#
-
# @return [Addressable::URI] The constructed URI object.
-
1
def initialize(options={})
-
if options.has_key?(:authority)
-
if (options.keys & [:userinfo, :user, :password, :host, :port]).any?
-
raise ArgumentError,
-
"Cannot specify both an authority and any of the components " +
-
"within the authority."
-
end
-
end
-
if options.has_key?(:userinfo)
-
if (options.keys & [:user, :password]).any?
-
raise ArgumentError,
-
"Cannot specify both a userinfo and either the user or password."
-
end
-
end
-
-
self.defer_validation do
-
# Bunch of crazy logic required because of the composite components
-
# like userinfo and authority.
-
self.scheme = options[:scheme] if options[:scheme]
-
self.user = options[:user] if options[:user]
-
self.password = options[:password] if options[:password]
-
self.userinfo = options[:userinfo] if options[:userinfo]
-
self.host = options[:host] if options[:host]
-
self.port = options[:port] if options[:port]
-
self.authority = options[:authority] if options[:authority]
-
self.path = options[:path] if options[:path]
-
self.query = options[:query] if options[:query]
-
self.query_values = options[:query_values] if options[:query_values]
-
self.fragment = options[:fragment] if options[:fragment]
-
end
-
end
-
-
##
-
# Freeze URI, initializing instance variables.
-
#
-
# @return [Addressable::URI] The frozen URI object.
-
1
def freeze
-
self.normalized_scheme
-
self.normalized_user
-
self.normalized_password
-
self.normalized_userinfo
-
self.normalized_host
-
self.normalized_port
-
self.normalized_authority
-
self.normalized_site
-
self.normalized_path
-
self.normalized_query
-
self.normalized_fragment
-
self.hash
-
super
-
end
-
-
##
-
# The scheme component for this URI.
-
#
-
# @return [String] The scheme component.
-
1
def scheme
-
return instance_variable_defined?(:@scheme) ? @scheme : nil
-
end
-
-
##
-
# The scheme component for this URI, normalized.
-
#
-
# @return [String] The scheme component, normalized.
-
1
def normalized_scheme
-
self.scheme && @normalized_scheme ||= (begin
-
if self.scheme =~ /^\s*ssh\+svn\s*$/i
-
"svn+ssh"
-
else
-
Addressable::URI.normalize_component(
-
self.scheme.strip.downcase,
-
Addressable::URI::CharacterClasses::SCHEME
-
)
-
end
-
end)
-
end
-
-
##
-
# Sets the scheme component for this URI.
-
#
-
# @param [String, #to_str] new_scheme The new scheme component.
-
1
def scheme=(new_scheme)
-
if new_scheme && !new_scheme.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{new_scheme.class} into String."
-
elsif new_scheme
-
new_scheme = new_scheme.to_str
-
end
-
if new_scheme && new_scheme !~ /[a-z][a-z0-9\.\+\-]*/i
-
raise InvalidURIError, "Invalid scheme format."
-
end
-
@scheme = new_scheme
-
@scheme = nil if @scheme.to_s.strip.empty?
-
-
# Reset dependant values
-
@normalized_scheme = nil
-
@uri_string = nil
-
@hash = nil
-
-
# Ensure we haven't created an invalid URI
-
validate()
-
end
-
-
##
-
# The user component for this URI.
-
#
-
# @return [String] The user component.
-
1
def user
-
return instance_variable_defined?(:@user) ? @user : nil
-
end
-
-
##
-
# The user component for this URI, normalized.
-
#
-
# @return [String] The user component, normalized.
-
1
def normalized_user
-
self.user && @normalized_user ||= (begin
-
if normalized_scheme =~ /https?/ && self.user.strip.empty? &&
-
(!self.password || self.password.strip.empty?)
-
nil
-
else
-
Addressable::URI.normalize_component(
-
self.user.strip,
-
Addressable::URI::CharacterClasses::UNRESERVED
-
)
-
end
-
end)
-
end
-
-
##
-
# Sets the user component for this URI.
-
#
-
# @param [String, #to_str] new_user The new user component.
-
1
def user=(new_user)
-
if new_user && !new_user.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{new_user.class} into String."
-
end
-
@user = new_user ? new_user.to_str : nil
-
-
# You can't have a nil user with a non-nil password
-
if password != nil
-
@user = EMPTYSTR if @user.nil?
-
end
-
-
# Reset dependant values
-
@userinfo = nil
-
@normalized_userinfo = nil
-
@authority = nil
-
@normalized_user = nil
-
@uri_string = nil
-
@hash = nil
-
-
# Ensure we haven't created an invalid URI
-
validate()
-
end
-
-
##
-
# The password component for this URI.
-
#
-
# @return [String] The password component.
-
1
def password
-
return instance_variable_defined?(:@password) ? @password : nil
-
end
-
-
##
-
# The password component for this URI, normalized.
-
#
-
# @return [String] The password component, normalized.
-
1
def normalized_password
-
self.password && @normalized_password ||= (begin
-
if self.normalized_scheme =~ /https?/ && self.password.strip.empty? &&
-
(!self.user || self.user.strip.empty?)
-
nil
-
else
-
Addressable::URI.normalize_component(
-
self.password.strip,
-
Addressable::URI::CharacterClasses::UNRESERVED
-
)
-
end
-
end)
-
end
-
-
##
-
# Sets the password component for this URI.
-
#
-
# @param [String, #to_str] new_password The new password component.
-
1
def password=(new_password)
-
if new_password && !new_password.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{new_password.class} into String."
-
end
-
@password = new_password ? new_password.to_str : nil
-
-
# You can't have a nil user with a non-nil password
-
@password ||= nil
-
@user ||= nil
-
if @password != nil
-
@user = EMPTYSTR if @user.nil?
-
end
-
-
# Reset dependant values
-
@userinfo = nil
-
@normalized_userinfo = nil
-
@authority = nil
-
@normalized_password = nil
-
@uri_string = nil
-
@hash = nil
-
-
# Ensure we haven't created an invalid URI
-
validate()
-
end
-
-
##
-
# The userinfo component for this URI.
-
# Combines the user and password components.
-
#
-
# @return [String] The userinfo component.
-
1
def userinfo
-
current_user = self.user
-
current_password = self.password
-
(current_user || current_password) && @userinfo ||= (begin
-
if current_user && current_password
-
"#{current_user}:#{current_password}"
-
elsif current_user && !current_password
-
"#{current_user}"
-
end
-
end)
-
end
-
-
##
-
# The userinfo component for this URI, normalized.
-
#
-
# @return [String] The userinfo component, normalized.
-
1
def normalized_userinfo
-
self.userinfo && @normalized_userinfo ||= (begin
-
current_user = self.normalized_user
-
current_password = self.normalized_password
-
if !current_user && !current_password
-
nil
-
elsif current_user && current_password
-
"#{current_user}:#{current_password}"
-
elsif current_user && !current_password
-
"#{current_user}"
-
end
-
end)
-
end
-
-
##
-
# Sets the userinfo component for this URI.
-
#
-
# @param [String, #to_str] new_userinfo The new userinfo component.
-
1
def userinfo=(new_userinfo)
-
if new_userinfo && !new_userinfo.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{new_userinfo.class} into String."
-
end
-
new_user, new_password = if new_userinfo
-
[
-
new_userinfo.to_str.strip[/^(.*):/, 1],
-
new_userinfo.to_str.strip[/:(.*)$/, 1]
-
]
-
else
-
[nil, nil]
-
end
-
-
# Password assigned first to ensure validity in case of nil
-
self.password = new_password
-
self.user = new_user
-
-
# Reset dependant values
-
@authority = nil
-
@uri_string = nil
-
@hash = nil
-
-
# Ensure we haven't created an invalid URI
-
validate()
-
end
-
-
##
-
# The host component for this URI.
-
#
-
# @return [String] The host component.
-
1
def host
-
return instance_variable_defined?(:@host) ? @host : nil
-
end
-
-
##
-
# The host component for this URI, normalized.
-
#
-
# @return [String] The host component, normalized.
-
1
def normalized_host
-
self.host && @normalized_host ||= (begin
-
if self.host != nil
-
if !self.host.strip.empty?
-
result = ::Addressable::IDNA.to_ascii(
-
URI.unencode_component(self.host.strip.downcase)
-
)
-
if result[-1..-1] == "."
-
# Trailing dots are unnecessary
-
result = result[0...-1]
-
end
-
result
-
else
-
EMPTYSTR
-
end
-
else
-
nil
-
end
-
end)
-
end
-
-
##
-
# Sets the host component for this URI.
-
#
-
# @param [String, #to_str] new_host The new host component.
-
1
def host=(new_host)
-
if new_host && !new_host.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{new_host.class} into String."
-
end
-
@host = new_host ? new_host.to_str : nil
-
-
# Reset dependant values
-
@authority = nil
-
@normalized_host = nil
-
@uri_string = nil
-
@hash = nil
-
-
# Ensure we haven't created an invalid URI
-
validate()
-
end
-
-
##
-
# @see Addressable::URI#host
-
1
alias_method :hostname, :host
-
-
##
-
# @see Addressable::URI#host=
-
1
alias_method :hostname=, :host=
-
-
##
-
# The authority component for this URI.
-
# Combines the user, password, host, and port components.
-
#
-
# @return [String] The authority component.
-
1
def authority
-
self.host && @authority ||= (begin
-
authority = ""
-
if self.userinfo != nil
-
authority << "#{self.userinfo}@"
-
end
-
authority << self.host
-
if self.port != nil
-
authority << ":#{self.port}"
-
end
-
authority
-
end)
-
end
-
-
##
-
# The authority component for this URI, normalized.
-
#
-
# @return [String] The authority component, normalized.
-
1
def normalized_authority
-
self.authority && @normalized_authority ||= (begin
-
authority = ""
-
if self.normalized_userinfo != nil
-
authority << "#{self.normalized_userinfo}@"
-
end
-
authority << self.normalized_host
-
if self.normalized_port != nil
-
authority << ":#{self.normalized_port}"
-
end
-
authority
-
end)
-
end
-
-
##
-
# Sets the authority component for this URI.
-
#
-
# @param [String, #to_str] new_authority The new authority component.
-
1
def authority=(new_authority)
-
if new_authority
-
if !new_authority.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{new_authority.class} into String."
-
end
-
new_authority = new_authority.to_str
-
new_userinfo = new_authority[/^([^\[\]]*)@/, 1]
-
if new_userinfo
-
new_user = new_userinfo.strip[/^([^:]*):?/, 1]
-
new_password = new_userinfo.strip[/:(.*)$/, 1]
-
end
-
new_host =
-
new_authority.gsub(/^([^\[\]]*)@/, EMPTYSTR).gsub(/:([^:@\[\]]*?)$/, EMPTYSTR)
-
new_port =
-
new_authority[/:([^:@\[\]]*?)$/, 1]
-
end
-
-
# Password assigned first to ensure validity in case of nil
-
self.password = defined?(new_password) ? new_password : nil
-
self.user = defined?(new_user) ? new_user : nil
-
self.host = defined?(new_host) ? new_host : nil
-
self.port = defined?(new_port) ? new_port : nil
-
-
# Reset dependant values
-
@userinfo = nil
-
@normalized_userinfo = nil
-
@uri_string = nil
-
@hash = nil
-
-
# Ensure we haven't created an invalid URI
-
validate()
-
end
-
-
##
-
# The origin for this URI, serialized to ASCII, as per
-
# draft-ietf-websec-origin-00, section 5.2.
-
#
-
# @return [String] The serialized origin.
-
1
def origin
-
return (if self.scheme && self.authority
-
if self.normalized_port
-
(
-
"#{self.normalized_scheme}://#{self.normalized_host}" +
-
":#{self.normalized_port}"
-
)
-
else
-
"#{self.normalized_scheme}://#{self.normalized_host}"
-
end
-
else
-
"null"
-
end)
-
end
-
-
# Returns an array of known ip-based schemes. These schemes typically
-
# use a similar URI form:
-
# <code>//<user>:<password>@<host>:<port>/<url-path></code>
-
1
def self.ip_based_schemes
-
return self.port_mapping.keys
-
end
-
-
# Returns a hash of common IP-based schemes and their default port
-
# numbers. Adding new schemes to this hash, as necessary, will allow
-
# for better URI normalization.
-
1
def self.port_mapping
-
PORT_MAPPING
-
end
-
-
##
-
# The port component for this URI.
-
# This is the port number actually given in the URI. This does not
-
# infer port numbers from default values.
-
#
-
# @return [Integer] The port component.
-
1
def port
-
return instance_variable_defined?(:@port) ? @port : nil
-
end
-
-
##
-
# The port component for this URI, normalized.
-
#
-
# @return [Integer] The port component, normalized.
-
1
def normalized_port
-
if URI.port_mapping[self.normalized_scheme] == self.port
-
nil
-
else
-
self.port
-
end
-
end
-
-
##
-
# Sets the port component for this URI.
-
#
-
# @param [String, Integer, #to_s] new_port The new port component.
-
1
def port=(new_port)
-
if new_port != nil && new_port.respond_to?(:to_str)
-
new_port = Addressable::URI.unencode_component(new_port.to_str)
-
end
-
if new_port != nil && !(new_port.to_s =~ /^\d+$/)
-
raise InvalidURIError,
-
"Invalid port number: #{new_port.inspect}"
-
end
-
-
@port = new_port.to_s.to_i
-
@port = nil if @port == 0
-
-
# Reset dependant values
-
@authority = nil
-
@normalized_port = nil
-
@uri_string = nil
-
@hash = nil
-
-
# Ensure we haven't created an invalid URI
-
validate()
-
end
-
-
##
-
# The inferred port component for this URI.
-
# This method will normalize to the default port for the URI's scheme if
-
# the port isn't explicitly specified in the URI.
-
#
-
# @return [Integer] The inferred port component.
-
1
def inferred_port
-
if self.port.to_i == 0
-
if self.scheme
-
URI.port_mapping[self.scheme.strip.downcase]
-
else
-
nil
-
end
-
else
-
self.port.to_i
-
end
-
end
-
-
##
-
# The combination of components that represent a site.
-
# Combines the scheme, user, password, host, and port components.
-
# Primarily useful for HTTP and HTTPS.
-
#
-
# For example, <code>"http://example.com/path?query"</code> would have a
-
# <code>site</code> value of <code>"http://example.com"</code>.
-
#
-
# @return [String] The components that identify a site.
-
1
def site
-
(self.scheme || self.authority) && @site ||= (begin
-
site_string = ""
-
site_string << "#{self.scheme}:" if self.scheme != nil
-
site_string << "//#{self.authority}" if self.authority != nil
-
site_string
-
end)
-
end
-
-
##
-
# The normalized combination of components that represent a site.
-
# Combines the scheme, user, password, host, and port components.
-
# Primarily useful for HTTP and HTTPS.
-
#
-
# For example, <code>"http://example.com/path?query"</code> would have a
-
# <code>site</code> value of <code>"http://example.com"</code>.
-
#
-
# @return [String] The normalized components that identify a site.
-
1
def normalized_site
-
self.site && @normalized_site ||= (begin
-
site_string = ""
-
if self.normalized_scheme != nil
-
site_string << "#{self.normalized_scheme}:"
-
end
-
if self.normalized_authority != nil
-
site_string << "//#{self.normalized_authority}"
-
end
-
site_string
-
end)
-
end
-
-
##
-
# Sets the site value for this URI.
-
#
-
# @param [String, #to_str] new_site The new site value.
-
1
def site=(new_site)
-
if new_site
-
if !new_site.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{new_site.class} into String."
-
end
-
new_site = new_site.to_str
-
# These two regular expressions derived from the primary parsing
-
# expression
-
self.scheme = new_site[/^(?:([^:\/?#]+):)?(?:\/\/(?:[^\/?#]*))?$/, 1]
-
self.authority = new_site[
-
/^(?:(?:[^:\/?#]+):)?(?:\/\/([^\/?#]*))?$/, 1
-
]
-
else
-
self.scheme = nil
-
self.authority = nil
-
end
-
end
-
-
##
-
# The path component for this URI.
-
#
-
# @return [String] The path component.
-
1
def path
-
return instance_variable_defined?(:@path) ? @path : EMPTYSTR
-
end
-
-
1
NORMPATH = /^(?!\/)[^\/:]*:.*$/
-
##
-
# The path component for this URI, normalized.
-
#
-
# @return [String] The path component, normalized.
-
1
def normalized_path
-
@normalized_path ||= (begin
-
path = self.path.to_s
-
if self.scheme == nil && path =~ NORMPATH
-
# Relative paths with colons in the first segment are ambiguous.
-
path = path.sub(":", "%2F")
-
end
-
# String#split(delimeter, -1) uses the more strict splitting behavior
-
# found by default in Python.
-
result = (path.strip.split(SLASH, -1).map do |segment|
-
Addressable::URI.normalize_component(
-
segment,
-
Addressable::URI::CharacterClasses::PCHAR
-
)
-
end).join(SLASH)
-
-
result = URI.normalize_path(result)
-
if result.empty? &&
-
["http", "https", "ftp", "tftp"].include?(self.normalized_scheme)
-
result = SLASH
-
end
-
result
-
end)
-
end
-
-
##
-
# Sets the path component for this URI.
-
#
-
# @param [String, #to_str] new_path The new path component.
-
1
def path=(new_path)
-
if new_path && !new_path.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{new_path.class} into String."
-
end
-
@path = (new_path || EMPTYSTR).to_str
-
if !@path.empty? && @path[0..0] != SLASH && host != nil
-
@path = "/#{@path}"
-
end
-
-
# Reset dependant values
-
@normalized_path = nil
-
@uri_string = nil
-
@hash = nil
-
end
-
-
##
-
# The basename, if any, of the file in the path component.
-
#
-
# @return [String] The path's basename.
-
1
def basename
-
# Path cannot be nil
-
return File.basename(self.path).gsub(/;[^\/]*$/, EMPTYSTR)
-
end
-
-
##
-
# The extname, if any, of the file in the path component.
-
# Empty string if there is no extension.
-
#
-
# @return [String] The path's extname.
-
1
def extname
-
return nil unless self.path
-
return File.extname(self.basename)
-
end
-
-
##
-
# The query component for this URI.
-
#
-
# @return [String] The query component.
-
1
def query
-
return instance_variable_defined?(:@query) ? @query : nil
-
end
-
-
##
-
# The query component for this URI, normalized.
-
#
-
# @return [String] The query component, normalized.
-
1
def normalized_query
-
self.query && @normalized_query ||= (begin
-
(self.query.split("&", -1).map do |pair|
-
Addressable::URI.normalize_component(
-
pair,
-
Addressable::URI::CharacterClasses::QUERY.sub("\\&", "")
-
)
-
end).join("&")
-
end)
-
end
-
-
##
-
# Sets the query component for this URI.
-
#
-
# @param [String, #to_str] new_query The new query component.
-
1
def query=(new_query)
-
if new_query && !new_query.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{new_query.class} into String."
-
end
-
@query = new_query ? new_query.to_str : nil
-
-
# Reset dependant values
-
@normalized_query = nil
-
@uri_string = nil
-
@hash = nil
-
end
-
-
##
-
# Converts the query component to a Hash value.
-
#
-
# @option [Symbol] notation
-
# May be one of <code>:flat</code>, <code>:dot</code>, or
-
# <code>:subscript</code>. The <code>:dot</code> notation is not
-
# supported for assignment. Default value is <code>:subscript</code>.
-
#
-
# @return [Hash, Array] The query string parsed as a Hash or Array object.
-
#
-
# @example
-
# Addressable::URI.parse("?one=1&two=2&three=3").query_values
-
# #=> {"one" => "1", "two" => "2", "three" => "3"}
-
# Addressable::URI.parse("?one[two][three]=four").query_values
-
# #=> {"one" => {"two" => {"three" => "four"}}}
-
# Addressable::URI.parse("?one.two.three=four").query_values(
-
# :notation => :dot
-
# )
-
# #=> {"one" => {"two" => {"three" => "four"}}}
-
# Addressable::URI.parse("?one[two][three]=four").query_values(
-
# :notation => :flat
-
# )
-
# #=> {"one[two][three]" => "four"}
-
# Addressable::URI.parse("?one.two.three=four").query_values(
-
# :notation => :flat
-
# )
-
# #=> {"one.two.three" => "four"}
-
# Addressable::URI.parse(
-
# "?one[two][three][]=four&one[two][three][]=five"
-
# ).query_values
-
# #=> {"one" => {"two" => {"three" => ["four", "five"]}}}
-
# Addressable::URI.parse(
-
# "?one=two&one=three").query_values(:notation => :flat_array)
-
# #=> [['one', 'two'], ['one', 'three']]
-
1
def query_values(options={})
-
defaults = {:notation => :subscript}
-
options = defaults.merge(options)
-
if ![:flat, :dot, :subscript, :flat_array].include?(options[:notation])
-
raise ArgumentError,
-
"Invalid notation. Must be one of: " +
-
"[:flat, :dot, :subscript, :flat_array]."
-
end
-
dehash = lambda do |hash|
-
hash.each do |(key, value)|
-
if value.kind_of?(Hash)
-
hash[key] = dehash.call(value)
-
end
-
end
-
if hash != {} && hash.keys.all? { |key| key =~ /^\d+$/ }
-
hash.sort.inject([]) do |accu, (_, value)|
-
accu << value; accu
-
end
-
else
-
hash
-
end
-
end
-
return nil if self.query == nil
-
empty_accumulator = :flat_array == options[:notation] ? [] : {}
-
return ((self.query.split("&").map do |pair|
-
pair.split("=", 2) if pair && !pair.empty?
-
end).compact.inject(empty_accumulator.dup) do |accumulator, (key, value)|
-
value = true if value.nil?
-
key = URI.unencode_component(key)
-
if value != true
-
value = URI.unencode_component(value.gsub(/\+/, " "))
-
end
-
if options[:notation] == :flat
-
if accumulator[key]
-
raise ArgumentError, "Key was repeated: #{key.inspect}"
-
end
-
accumulator[key] = value
-
elsif options[:notation] == :flat_array
-
accumulator << [key, value]
-
else
-
if options[:notation] == :dot
-
array_value = false
-
subkeys = key.split(".")
-
elsif options[:notation] == :subscript
-
array_value = !!(key =~ /\[\]$/)
-
subkeys = key.split(/[\[\]]+/)
-
end
-
current_hash = accumulator
-
for i in 0...(subkeys.size - 1)
-
subkey = subkeys[i]
-
current_hash[subkey] = {} unless current_hash[subkey]
-
current_hash = current_hash[subkey]
-
end
-
if array_value
-
current_hash[subkeys.last] = [] unless current_hash[subkeys.last]
-
current_hash[subkeys.last] << value
-
else
-
current_hash[subkeys.last] = value
-
end
-
end
-
accumulator
-
end).inject(empty_accumulator.dup) do |accumulator, (key, value)|
-
if options[:notation] == :flat_array
-
accumulator << [key, value]
-
else
-
accumulator[key] = value.kind_of?(Hash) ? dehash.call(value) : value
-
end
-
accumulator
-
end
-
end
-
-
##
-
# Sets the query component for this URI from a Hash object.
-
# This method produces a query string using the :subscript notation.
-
# An empty Hash will result in a nil query.
-
#
-
# @param [Hash, #to_hash, Array] new_query_values The new query values.
-
1
def query_values=(new_query_values)
-
if new_query_values == nil
-
self.query = nil
-
return nil
-
end
-
-
if !new_query_values.is_a?(Array)
-
if !new_query_values.respond_to?(:to_hash)
-
raise TypeError,
-
"Can't convert #{new_query_values.class} into Hash."
-
end
-
new_query_values = new_query_values.to_hash
-
new_query_values = new_query_values.map do |key, value|
-
key = key.to_s if key.kind_of?(Symbol)
-
[key, value]
-
end
-
# Useful default for OAuth and caching.
-
# Only to be used for non-Array inputs. Arrays should preserve order.
-
new_query_values.sort!
-
end
-
-
##
-
# Joins and converts parent and value into a properly encoded and
-
# ordered URL query.
-
#
-
# @private
-
# @param [String] parent an URI encoded component.
-
# @param [Array, Hash, Symbol, #to_str] value
-
#
-
# @return [String] a properly escaped and ordered URL query.
-
to_query = lambda do |parent, value|
-
if value.is_a?(Hash)
-
value = value.map do |key, val|
-
[
-
URI.encode_component(key, CharacterClasses::UNRESERVED),
-
val
-
]
-
end
-
value.sort!
-
buffer = ""
-
value.each do |key, val|
-
new_parent = "#{parent}[#{key}]"
-
buffer << "#{to_query.call(new_parent, val)}&"
-
end
-
return buffer.chop
-
elsif value.is_a?(Array)
-
buffer = ""
-
value.each_with_index do |val, i|
-
new_parent = "#{parent}[#{i}]"
-
buffer << "#{to_query.call(new_parent, val)}&"
-
end
-
return buffer.chop
-
elsif value == true
-
return parent
-
else
-
encoded_value = URI.encode_component(
-
value, CharacterClasses::UNRESERVED
-
)
-
return "#{parent}=#{encoded_value}"
-
end
-
end
-
-
# new_query_values have form [['key1', 'value1'], ['key2', 'value2']]
-
buffer = ""
-
new_query_values.each do |parent, value|
-
encoded_parent = URI.encode_component(
-
parent, CharacterClasses::UNRESERVED
-
)
-
buffer << "#{to_query.call(encoded_parent, value)}&"
-
end
-
self.query = buffer.chop
-
end
-
-
##
-
# The HTTP request URI for this URI. This is the path and the
-
# query string.
-
#
-
# @return [String] The request URI required for an HTTP request.
-
1
def request_uri
-
return nil if self.absolute? && self.scheme !~ /^https?$/
-
return (
-
(!self.path.empty? ? self.path : SLASH) +
-
(self.query ? "?#{self.query}" : EMPTYSTR)
-
)
-
end
-
-
##
-
# Sets the HTTP request URI for this URI.
-
#
-
# @param [String, #to_str] new_request_uri The new HTTP request URI.
-
1
def request_uri=(new_request_uri)
-
if !new_request_uri.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{new_request_uri.class} into String."
-
end
-
if self.absolute? && self.scheme !~ /^https?$/
-
raise InvalidURIError,
-
"Cannot set an HTTP request URI for a non-HTTP URI."
-
end
-
new_request_uri = new_request_uri.to_str
-
path_component = new_request_uri[/^([^\?]*)\?(?:.*)$/, 1]
-
query_component = new_request_uri[/^(?:[^\?]*)\?(.*)$/, 1]
-
path_component = path_component.to_s
-
path_component = (!path_component.empty? ? path_component : SLASH)
-
self.path = path_component
-
self.query = query_component
-
-
# Reset dependant values
-
@uri_string = nil
-
@hash = nil
-
end
-
-
##
-
# The fragment component for this URI.
-
#
-
# @return [String] The fragment component.
-
1
def fragment
-
return instance_variable_defined?(:@fragment) ? @fragment : nil
-
end
-
-
##
-
# The fragment component for this URI, normalized.
-
#
-
# @return [String] The fragment component, normalized.
-
1
def normalized_fragment
-
self.fragment && @normalized_fragment ||= (begin
-
Addressable::URI.normalize_component(
-
self.fragment.strip,
-
Addressable::URI::CharacterClasses::FRAGMENT
-
)
-
end)
-
end
-
-
##
-
# Sets the fragment component for this URI.
-
#
-
# @param [String, #to_str] new_fragment The new fragment component.
-
1
def fragment=(new_fragment)
-
if new_fragment && !new_fragment.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{new_fragment.class} into String."
-
end
-
@fragment = new_fragment ? new_fragment.to_str : nil
-
-
# Reset dependant values
-
@normalized_fragment = nil
-
@uri_string = nil
-
@hash = nil
-
-
# Ensure we haven't created an invalid URI
-
validate()
-
end
-
-
##
-
# Determines if the scheme indicates an IP-based protocol.
-
#
-
# @return [TrueClass, FalseClass]
-
# <code>true</code> if the scheme indicates an IP-based protocol.
-
# <code>false</code> otherwise.
-
1
def ip_based?
-
if self.scheme
-
return URI.ip_based_schemes.include?(
-
self.scheme.strip.downcase)
-
end
-
return false
-
end
-
-
##
-
# Determines if the URI is relative.
-
#
-
# @return [TrueClass, FalseClass]
-
# <code>true</code> if the URI is relative. <code>false</code>
-
# otherwise.
-
1
def relative?
-
return self.scheme.nil?
-
end
-
-
##
-
# Determines if the URI is absolute.
-
#
-
# @return [TrueClass, FalseClass]
-
# <code>true</code> if the URI is absolute. <code>false</code>
-
# otherwise.
-
1
def absolute?
-
return !relative?
-
end
-
-
##
-
# Joins two URIs together.
-
#
-
# @param [String, Addressable::URI, #to_str] The URI to join with.
-
#
-
# @return [Addressable::URI] The joined URI.
-
1
def join(uri)
-
if !uri.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{uri.class} into String."
-
end
-
if !uri.kind_of?(URI)
-
# Otherwise, convert to a String, then parse.
-
uri = URI.parse(uri.to_str)
-
end
-
if uri.to_s.empty?
-
return self.dup
-
end
-
-
joined_scheme = nil
-
joined_user = nil
-
joined_password = nil
-
joined_host = nil
-
joined_port = nil
-
joined_path = nil
-
joined_query = nil
-
joined_fragment = nil
-
-
# Section 5.2.2 of RFC 3986
-
if uri.scheme != nil
-
joined_scheme = uri.scheme
-
joined_user = uri.user
-
joined_password = uri.password
-
joined_host = uri.host
-
joined_port = uri.port
-
joined_path = URI.normalize_path(uri.path)
-
joined_query = uri.query
-
else
-
if uri.authority != nil
-
joined_user = uri.user
-
joined_password = uri.password
-
joined_host = uri.host
-
joined_port = uri.port
-
joined_path = URI.normalize_path(uri.path)
-
joined_query = uri.query
-
else
-
if uri.path == nil || uri.path.empty?
-
joined_path = self.path
-
if uri.query != nil
-
joined_query = uri.query
-
else
-
joined_query = self.query
-
end
-
else
-
if uri.path[0..0] == SLASH
-
joined_path = URI.normalize_path(uri.path)
-
else
-
base_path = self.path.dup
-
base_path = EMPTYSTR if base_path == nil
-
base_path = URI.normalize_path(base_path)
-
-
# Section 5.2.3 of RFC 3986
-
#
-
# Removes the right-most path segment from the base path.
-
if base_path =~ /\//
-
base_path.gsub!(/\/[^\/]+$/, SLASH)
-
else
-
base_path = EMPTYSTR
-
end
-
-
# If the base path is empty and an authority segment has been
-
# defined, use a base path of SLASH
-
if base_path.empty? && self.authority != nil
-
base_path = SLASH
-
end
-
-
joined_path = URI.normalize_path(base_path + uri.path)
-
end
-
joined_query = uri.query
-
end
-
joined_user = self.user
-
joined_password = self.password
-
joined_host = self.host
-
joined_port = self.port
-
end
-
joined_scheme = self.scheme
-
end
-
joined_fragment = uri.fragment
-
-
return Addressable::URI.new(
-
:scheme => joined_scheme,
-
:user => joined_user,
-
:password => joined_password,
-
:host => joined_host,
-
:port => joined_port,
-
:path => joined_path,
-
:query => joined_query,
-
:fragment => joined_fragment
-
)
-
end
-
1
alias_method :+, :join
-
-
##
-
# Destructive form of <code>join</code>.
-
#
-
# @param [String, Addressable::URI, #to_str] The URI to join with.
-
#
-
# @return [Addressable::URI] The joined URI.
-
#
-
# @see Addressable::URI#join
-
1
def join!(uri)
-
replace_self(self.join(uri))
-
end
-
-
##
-
# Merges a URI with a <code>Hash</code> of components.
-
# This method has different behavior from <code>join</code>. Any
-
# components present in the <code>hash</code> parameter will override the
-
# original components. The path component is not treated specially.
-
#
-
# @param [Hash, Addressable::URI, #to_hash] The components to merge with.
-
#
-
# @return [Addressable::URI] The merged URI.
-
#
-
# @see Hash#merge
-
1
def merge(hash)
-
if !hash.respond_to?(:to_hash)
-
raise TypeError, "Can't convert #{hash.class} into Hash."
-
end
-
hash = hash.to_hash
-
-
if hash.has_key?(:authority)
-
if (hash.keys & [:userinfo, :user, :password, :host, :port]).any?
-
raise ArgumentError,
-
"Cannot specify both an authority and any of the components " +
-
"within the authority."
-
end
-
end
-
if hash.has_key?(:userinfo)
-
if (hash.keys & [:user, :password]).any?
-
raise ArgumentError,
-
"Cannot specify both a userinfo and either the user or password."
-
end
-
end
-
-
uri = Addressable::URI.new
-
uri.defer_validation do
-
# Bunch of crazy logic required because of the composite components
-
# like userinfo and authority.
-
uri.scheme =
-
hash.has_key?(:scheme) ? hash[:scheme] : self.scheme
-
if hash.has_key?(:authority)
-
uri.authority =
-
hash.has_key?(:authority) ? hash[:authority] : self.authority
-
end
-
if hash.has_key?(:userinfo)
-
uri.userinfo =
-
hash.has_key?(:userinfo) ? hash[:userinfo] : self.userinfo
-
end
-
if !hash.has_key?(:userinfo) && !hash.has_key?(:authority)
-
uri.user =
-
hash.has_key?(:user) ? hash[:user] : self.user
-
uri.password =
-
hash.has_key?(:password) ? hash[:password] : self.password
-
end
-
if !hash.has_key?(:authority)
-
uri.host =
-
hash.has_key?(:host) ? hash[:host] : self.host
-
uri.port =
-
hash.has_key?(:port) ? hash[:port] : self.port
-
end
-
uri.path =
-
hash.has_key?(:path) ? hash[:path] : self.path
-
uri.query =
-
hash.has_key?(:query) ? hash[:query] : self.query
-
uri.fragment =
-
hash.has_key?(:fragment) ? hash[:fragment] : self.fragment
-
end
-
-
return uri
-
end
-
-
##
-
# Destructive form of <code>merge</code>.
-
#
-
# @param [Hash, Addressable::URI, #to_hash] The components to merge with.
-
#
-
# @return [Addressable::URI] The merged URI.
-
#
-
# @see Addressable::URI#merge
-
1
def merge!(uri)
-
replace_self(self.merge(uri))
-
end
-
-
##
-
# Returns the shortest normalized relative form of this URI that uses the
-
# supplied URI as a base for resolution. Returns an absolute URI if
-
# necessary. This is effectively the opposite of <code>route_to</code>.
-
#
-
# @param [String, Addressable::URI, #to_str] uri The URI to route from.
-
#
-
# @return [Addressable::URI]
-
# The normalized relative URI that is equivalent to the original URI.
-
1
def route_from(uri)
-
uri = URI.parse(uri).normalize
-
normalized_self = self.normalize
-
if normalized_self.relative?
-
raise ArgumentError, "Expected absolute URI, got: #{self.to_s}"
-
end
-
if uri.relative?
-
raise ArgumentError, "Expected absolute URI, got: #{uri.to_s}"
-
end
-
if normalized_self == uri
-
return Addressable::URI.parse("##{normalized_self.fragment}")
-
end
-
components = normalized_self.to_hash
-
if normalized_self.scheme == uri.scheme
-
components[:scheme] = nil
-
if normalized_self.authority == uri.authority
-
components[:user] = nil
-
components[:password] = nil
-
components[:host] = nil
-
components[:port] = nil
-
if normalized_self.path == uri.path
-
components[:path] = nil
-
if normalized_self.query == uri.query
-
components[:query] = nil
-
end
-
else
-
if uri.path != SLASH
-
components[:path].gsub!(
-
Regexp.new("^" + Regexp.escape(uri.path)), EMPTYSTR)
-
end
-
end
-
end
-
end
-
# Avoid network-path references.
-
if components[:host] != nil
-
components[:scheme] = normalized_self.scheme
-
end
-
return Addressable::URI.new(
-
:scheme => components[:scheme],
-
:user => components[:user],
-
:password => components[:password],
-
:host => components[:host],
-
:port => components[:port],
-
:path => components[:path],
-
:query => components[:query],
-
:fragment => components[:fragment]
-
)
-
end
-
-
##
-
# Returns the shortest normalized relative form of the supplied URI that
-
# uses this URI as a base for resolution. Returns an absolute URI if
-
# necessary. This is effectively the opposite of <code>route_from</code>.
-
#
-
# @param [String, Addressable::URI, #to_str] uri The URI to route to.
-
#
-
# @return [Addressable::URI]
-
# The normalized relative URI that is equivalent to the supplied URI.
-
1
def route_to(uri)
-
return URI.parse(uri).route_from(self)
-
end
-
-
##
-
# Returns a normalized URI object.
-
#
-
# NOTE: This method does not attempt to fully conform to specifications.
-
# It exists largely to correct other people's failures to read the
-
# specifications, and also to deal with caching issues since several
-
# different URIs may represent the same resource and should not be
-
# cached multiple times.
-
#
-
# @return [Addressable::URI] The normalized URI.
-
1
def normalize
-
# This is a special exception for the frequently misused feed
-
# URI scheme.
-
if normalized_scheme == "feed"
-
if self.to_s =~ /^feed:\/*http:\/*/
-
return URI.parse(
-
self.to_s[/^feed:\/*(http:\/*.*)/, 1]
-
).normalize
-
end
-
end
-
-
return Addressable::URI.new(
-
:scheme => normalized_scheme,
-
:authority => normalized_authority,
-
:path => normalized_path,
-
:query => normalized_query,
-
:fragment => normalized_fragment
-
)
-
end
-
-
##
-
# Destructively normalizes this URI object.
-
#
-
# @return [Addressable::URI] The normalized URI.
-
#
-
# @see Addressable::URI#normalize
-
1
def normalize!
-
replace_self(self.normalize)
-
end
-
-
##
-
# Creates a URI suitable for display to users. If semantic attacks are
-
# likely, the application should try to detect these and warn the user.
-
# See <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>,
-
# section 7.6 for more information.
-
#
-
# @return [Addressable::URI] A URI suitable for display purposes.
-
1
def display_uri
-
display_uri = self.normalize
-
display_uri.host = ::Addressable::IDNA.to_unicode(display_uri.host)
-
return display_uri
-
end
-
-
##
-
# Returns <code>true</code> if the URI objects are equal. This method
-
# normalizes both URIs before doing the comparison, and allows comparison
-
# against <code>Strings</code>.
-
#
-
# @param [Object] uri The URI to compare.
-
#
-
# @return [TrueClass, FalseClass]
-
# <code>true</code> if the URIs are equivalent, <code>false</code>
-
# otherwise.
-
1
def ===(uri)
-
if uri.respond_to?(:normalize)
-
uri_string = uri.normalize.to_s
-
else
-
begin
-
uri_string = ::Addressable::URI.parse(uri).normalize.to_s
-
rescue InvalidURIError, TypeError
-
return false
-
end
-
end
-
return self.normalize.to_s == uri_string
-
end
-
-
##
-
# Returns <code>true</code> if the URI objects are equal. This method
-
# normalizes both URIs before doing the comparison.
-
#
-
# @param [Object] uri The URI to compare.
-
#
-
# @return [TrueClass, FalseClass]
-
# <code>true</code> if the URIs are equivalent, <code>false</code>
-
# otherwise.
-
1
def ==(uri)
-
return false unless uri.kind_of?(URI)
-
return self.normalize.to_s == uri.normalize.to_s
-
end
-
-
##
-
# Returns <code>true</code> if the URI objects are equal. This method
-
# does NOT normalize either URI before doing the comparison.
-
#
-
# @param [Object] uri The URI to compare.
-
#
-
# @return [TrueClass, FalseClass]
-
# <code>true</code> if the URIs are equivalent, <code>false</code>
-
# otherwise.
-
1
def eql?(uri)
-
return false unless uri.kind_of?(URI)
-
return self.to_s == uri.to_s
-
end
-
-
##
-
# A hash value that will make a URI equivalent to its normalized
-
# form.
-
#
-
# @return [Integer] A hash of the URI.
-
1
def hash
-
return @hash ||= (self.to_s.hash * -1)
-
end
-
-
##
-
# Clones the URI object.
-
#
-
# @return [Addressable::URI] The cloned URI.
-
1
def dup
-
duplicated_uri = Addressable::URI.new(
-
:scheme => self.scheme ? self.scheme.dup : nil,
-
:user => self.user ? self.user.dup : nil,
-
:password => self.password ? self.password.dup : nil,
-
:host => self.host ? self.host.dup : nil,
-
:port => self.port,
-
:path => self.path ? self.path.dup : nil,
-
:query => self.query ? self.query.dup : nil,
-
:fragment => self.fragment ? self.fragment.dup : nil
-
)
-
return duplicated_uri
-
end
-
-
##
-
# Omits components from a URI.
-
#
-
# @param [Symbol] *components The components to be omitted.
-
#
-
# @return [Addressable::URI] The URI with components omitted.
-
#
-
# @example
-
# uri = Addressable::URI.parse("http://example.com/path?query")
-
# #=> #<Addressable::URI:0xcc5e7a URI:http://example.com/path?query>
-
# uri.omit(:scheme, :authority)
-
# #=> #<Addressable::URI:0xcc4d86 URI:/path?query>
-
1
def omit(*components)
-
invalid_components = components - [
-
:scheme, :user, :password, :userinfo, :host, :port, :authority,
-
:path, :query, :fragment
-
]
-
unless invalid_components.empty?
-
raise ArgumentError,
-
"Invalid component names: #{invalid_components.inspect}."
-
end
-
duplicated_uri = self.dup
-
duplicated_uri.defer_validation do
-
components.each do |component|
-
duplicated_uri.send((component.to_s + "=").to_sym, nil)
-
end
-
duplicated_uri.user = duplicated_uri.normalized_user
-
end
-
duplicated_uri
-
end
-
-
##
-
# Destructive form of omit.
-
#
-
# @param [Symbol] *components The components to be omitted.
-
#
-
# @return [Addressable::URI] The URI with components omitted.
-
#
-
# @see Addressable::URI#omit
-
1
def omit!(*components)
-
replace_self(self.omit(*components))
-
end
-
-
##
-
# Converts the URI to a <code>String</code>.
-
#
-
# @return [String] The URI's <code>String</code> representation.
-
1
def to_s
-
if self.scheme == nil && self.path != nil && !self.path.empty? &&
-
self.path =~ NORMPATH
-
raise InvalidURIError,
-
"Cannot assemble URI string with ambiguous path: '#{self.path}'"
-
end
-
@uri_string ||= (begin
-
uri_string = ""
-
uri_string << "#{self.scheme}:" if self.scheme != nil
-
uri_string << "//#{self.authority}" if self.authority != nil
-
uri_string << self.path.to_s
-
uri_string << "?#{self.query}" if self.query != nil
-
uri_string << "##{self.fragment}" if self.fragment != nil
-
if uri_string.respond_to?(:force_encoding)
-
uri_string.force_encoding(Encoding::UTF_8)
-
end
-
uri_string
-
end)
-
end
-
-
##
-
# URI's are glorified <code>Strings</code>. Allow implicit conversion.
-
1
alias_method :to_str, :to_s
-
-
##
-
# Returns a Hash of the URI components.
-
#
-
# @return [Hash] The URI as a <code>Hash</code> of components.
-
1
def to_hash
-
return {
-
:scheme => self.scheme,
-
:user => self.user,
-
:password => self.password,
-
:host => self.host,
-
:port => self.port,
-
:path => self.path,
-
:query => self.query,
-
:fragment => self.fragment
-
}
-
end
-
-
##
-
# Returns a <code>String</code> representation of the URI object's state.
-
#
-
# @return [String] The URI object's state, as a <code>String</code>.
-
1
def inspect
-
sprintf("#<%s:%#0x URI:%s>", URI.to_s, self.object_id, self.to_s)
-
end
-
-
##
-
# This method allows you to make several changes to a URI simultaneously,
-
# which separately would cause validation errors, but in conjunction,
-
# are valid. The URI will be revalidated as soon as the entire block has
-
# been executed.
-
#
-
# @param [Proc] block
-
# A set of operations to perform on a given URI.
-
1
def defer_validation(&block)
-
raise LocalJumpError, "No block given." unless block
-
@validation_deferred = true
-
block.call()
-
@validation_deferred = false
-
validate
-
return nil
-
end
-
-
1
private
-
##
-
# Resolves paths to their simplest form.
-
#
-
# @param [String] path The path to normalize.
-
#
-
# @return [String] The normalized path.
-
-
1
PARENT1 = '.'
-
1
PARENT2 = '..'
-
-
1
NPATH1 = /\/\.\/|\/\.$/
-
1
NPATH2 = /\/([^\/]+)\/\.\.\/|\/([^\/]+)\/\.\.$/
-
1
NPATH3 = /^\.\.?\/?/
-
1
NPATH4 = /^\/\.\.?\/|^(\/\.\.?)+\/?$/
-
-
1
def self.normalize_path(path)
-
# Section 5.2.4 of RFC 3986
-
-
return nil if path.nil?
-
normalized_path = path.dup
-
begin
-
mod = nil
-
mod ||= normalized_path.gsub!(NPATH1, SLASH)
-
-
parent = normalized_path.match(NPATH2)
-
if parent && ((parent[1] != PARENT1 && parent[1] != PARENT2) \
-
|| (parent[2] != PARENT1 && parent[2] != PARENT2))
-
mod ||= normalized_path.gsub!(/\/#{Regexp.escape(parent[1].to_s)}\/\.\.\/|(\/#{Regexp.escape(parent[2].to_s)}\/\.\.$)/, SLASH)
-
end
-
-
mod ||= normalized_path.gsub!(NPATH3, EMPTYSTR)
-
mod ||= normalized_path.gsub!(NPATH4, SLASH)
-
end until mod.nil?
-
-
return normalized_path
-
end
-
-
##
-
# Ensures that the URI is valid.
-
1
def validate
-
return if !!@validation_deferred
-
if self.scheme != nil &&
-
(self.host == nil || self.host.empty?) &&
-
(self.path == nil || self.path.empty?)
-
raise InvalidURIError,
-
"Absolute URI missing hierarchical segment: '#{self.to_s}'"
-
end
-
if self.host == nil
-
if self.port != nil ||
-
self.user != nil ||
-
self.password != nil
-
raise InvalidURIError, "Hostname not supplied: '#{self.to_s}'"
-
end
-
end
-
if self.path != nil && !self.path.empty? && self.path[0..0] != SLASH &&
-
self.authority != nil
-
raise InvalidURIError,
-
"Cannot have a relative path with an authority set: '#{self.to_s}'"
-
end
-
return nil
-
end
-
-
##
-
# Replaces the internal state of self with the specified URI's state.
-
# Used in destructive operations to avoid massive code repetition.
-
#
-
# @param [Addressable::URI] uri The URI to replace <code>self</code> with.
-
#
-
# @return [Addressable::URI] <code>self</code>.
-
1
def replace_self(uri)
-
# Reset dependant values
-
instance_variables.each do |var|
-
instance_variable_set(var, nil)
-
end
-
-
@scheme = uri.scheme
-
@user = uri.user
-
@password = uri.password
-
@host = uri.host
-
@port = uri.port
-
@path = uri.path
-
@query = uri.query
-
@fragment = uri.fragment
-
return self
-
end
-
end
-
end
-
# encoding:utf-8
-
#--
-
# Copyright (C) 2006-2011 Bob Aman
-
#
-
# Licensed under the Apache License, Version 2.0 (the "License");
-
# you may not use this file except in compliance with the License.
-
# You may obtain a copy of the License at
-
#
-
# http://www.apache.org/licenses/LICENSE-2.0
-
#
-
# Unless required by applicable law or agreed to in writing, software
-
# distributed under the License is distributed on an "AS IS" BASIS,
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-
# See the License for the specific language governing permissions and
-
# limitations under the License.
-
#++
-
-
-
# Used to prevent the class/module from being loaded more than once
-
1
if !defined?(Addressable::VERSION)
-
1
module Addressable
-
1
module VERSION
-
1
MAJOR = 2
-
1
MINOR = 2
-
1
TINY = 7
-
-
1
STRING = [MAJOR, MINOR, TINY].join('.')
-
end
-
end
-
end
-
1
module ANSI
-
-
1
require 'ansi/version'
-
-
# Table of codes used throughout the system.
-
#
-
# @see http://en.wikipedia.org/wiki/ANSI_escape_code
-
1
CHART = {
-
:clear => 0,
-
:reset => 0,
-
:bright => 1,
-
:bold => 1,
-
:faint => 2,
-
:dark => 2,
-
:italic => 3,
-
:underline => 4,
-
:underscore => 4,
-
:blink => 5,
-
:slow_blink => 5,
-
:rapid => 6,
-
:rapid_blink => 6,
-
:invert => 7,
-
:inverse => 7,
-
:reverse => 7,
-
:negative => 7,
-
:concealed => 8,
-
:swap => 7,
-
:conceal => 8,
-
:concealed => 8,
-
:hide => 9,
-
:strike => 9,
-
-
:default_font => 10,
-
:font_default => 10,
-
:font0 => 10,
-
:font1 => 11,
-
:font2 => 12,
-
:font3 => 13,
-
:font4 => 14,
-
:font5 => 15,
-
:font6 => 16,
-
:font7 => 17,
-
:font8 => 18,
-
:font9 => 19,
-
:fraktur => 20,
-
:bright_off => 21,
-
:bold_off => 21,
-
:double_underline => 21,
-
:clean => 22,
-
:italic_off => 23,
-
:fraktur_off => 23,
-
:underline_off => 24,
-
:blink_off => 25,
-
:inverse_off => 26,
-
:positive => 26,
-
:conceal_off => 27,
-
:show => 27,
-
:reveal => 27,
-
:crossed_off => 29,
-
:crossed_out_off => 29,
-
-
:black => 30,
-
:red => 31,
-
:green => 32,
-
:yellow => 33,
-
:blue => 34,
-
:magenta => 35,
-
:cyan => 36,
-
:white => 37,
-
-
:on_black => 40,
-
:on_red => 41,
-
:on_green => 42,
-
:on_yellow => 43,
-
:on_blue => 44,
-
:on_magenta => 45,
-
:on_cyan => 46,
-
:on_white => 47,
-
-
:frame => 51,
-
:encircle => 52,
-
:overline => 53,
-
:frame_off => 54,
-
:encircle_off => 54,
-
:overline_off => 55,
-
}
-
-
#
-
1
SPECIAL_CHART = {
-
:save => "\e[s", # Save current cursor positon.
-
:restore => "\e[u", # Restore saved cursor positon.
-
:clear_line => "\e[K", # Clear to the end of the current line.
-
:clr => "\e[K", # Clear to the end of the current line.
-
:clear_screen => "\e[2J", # Clear the screen and move cursor to home.
-
:cls => "\e[2J", # Clear the screen and move cursor to home.
-
}
-
-
end
-
1
module ANSI
-
-
1
if RUBY_PLATFORM =~ /(win32|w32)/
-
begin
-
require 'Win32/Console/ANSI'
-
rescue
-
warn "ansi: 'gem install win32console' to use color on Windows"
-
$ansi = false
-
end
-
end
-
-
1
require 'ansi/constants'
-
-
# Global variialbe can be used to prevent ANSI codes
-
# from being used in ANSI's methods that do so to string.
-
#
-
# NOTE: This has no effect of methods that return ANSI codes.
-
1
$ansi = true
-
-
# TODO: up, down, right, left, etc could have yielding methods too?
-
-
# ANSI Codes
-
#
-
# Ansi::Code module makes it very easy to use ANSI codes.
-
# These are esspecially nice for beautifying shell output.
-
#
-
# Ansi::Code.red + "Hello" + Ansi::Code.blue + "World"
-
# => "\e[31mHello\e[34mWorld"
-
#
-
# Ansi::Code.red{ "Hello" } + Ansi::Code.blue{ "World" }
-
# => "\e[31mHello\e[0m\e[34mWorld\e[0m"
-
#
-
# IMPORTANT! Do not mixin Ansi::Code, instead use {ANSI::Mixin}.
-
#
-
# See {ANSI::Code::CHART} for list of all supported codes.
-
#
-
1
module Code
-
1
extend self
-
-
# include ANSI Constants
-
1
include Constants
-
-
# Regexp for matching most ANSI codes.
-
1
PATTERN = /\e\[(\d+)m/
-
-
# ANSI clear code.
-
1
ENDCODE = "\e[0m"
-
-
# List of primary styles.
-
1
def self.styles
-
%w{bold dark italic underline underscore blink rapid reverse negative concealed strike}
-
end
-
-
# List of primary colors.
-
1
def self.colors
-
9
%w{black red green yellow blue magenta cyan white}
-
end
-
-
=begin
-
styles.each do |style|
-
module_eval <<-END, __FILE__, __LINE__
-
def #{style}(string=nil)
-
if string
-
return string unless $ansi
-
#warn "use ANSI block notation for future versions"
-
return "\#{#{style.upcase}}\#{string}\#{ENDCODE}"
-
end
-
if block_given?
-
return yield unless $ansi
-
return "\#{#{style.upcase}}\#{yield}\#{ENDCODE}"
-
end
-
#{style.upcase}
-
end
-
END
-
end
-
=end
-
-
=begin
-
# Dynamically create color methods.
-
-
colors.each do |color|
-
module_eval <<-END, __FILE__, __LINE__
-
def #{color}(string=nil)
-
if string
-
return string unless $ansi
-
#warn "use ANSI block notation for future versions"
-
return "\#{#{color.upcase}}\#{string}\#{ENDCODE}"
-
end
-
if block_given?
-
return yield unless $ansi
-
return "\#{#{color.upcase}}\#{yield}\#{ENDCODE}"
-
end
-
#{color.upcase}
-
end
-
-
def on_#{color}(string=nil)
-
if string
-
return string unless $ansi
-
#warn "use ANSI block notation for future versions"
-
return "\#{ON_#{color.upcase}}\#{string}\#{ENDCODE}"
-
end
-
if block_given?
-
return yield unless $ansi
-
return "\#{ON_#{color.upcase}}\#{yield}\#{ENDCODE}"
-
end
-
ON_#{color.upcase}
-
end
-
END
-
end
-
=end
-
-
# Return ANSI code given a list of symbolic names.
-
1
def [](*codes)
-
code(*codes)
-
end
-
-
# Dynamically create color on color methods.
-
#
-
# @deprecated
-
#
-
1
colors.each do |color|
-
8
colors.each do |on_color|
-
64
module_eval <<-END, __FILE__, __LINE__
-
def #{color}_on_#{on_color}(string=nil)
-
if string
-
return string unless $ansi
-
#warn "use ANSI block notation for future versions"
-
return #{color.upcase} + ON_#{color.upcase} + string + ENDCODE
-
end
-
if block_given?
-
return yield unless $ansi
-
#{color.upcase} + ON_#{on_color.upcase} + yield.to_s + ENDCODE
-
else
-
#{color.upcase} + ON_#{on_color.upcase}
-
end
-
end
-
END
-
end
-
end
-
-
# Use method missing to dispatch ANSI code methods.
-
1
def method_missing(code, *args, &blk)
-
esc = nil
-
-
if CHART.key?(code)
-
esc = "\e[#{CHART[code]}m"
-
elsif SPECIAL_CHART.key?(code)
-
esc = SPECIAL_CHART[code]
-
end
-
-
if esc
-
if string = args.first
-
return string unless $ansi
-
#warn "use ANSI block notation for future versions"
-
return "#{esc}#{string}#{ENDCODE}"
-
end
-
if block_given?
-
return yield unless $ansi
-
return "#{esc}#{yield}#{ENDCODE}"
-
end
-
esc
-
else
-
super(code, *args, &blk)
-
end
-
end
-
-
# TODO: How to deal with position codes when $ansi is false?
-
# Should we reaise an error or just not push the codes?
-
# For now, we will leave this it as is.
-
-
# Like +move+ but returns to original positon after
-
# yielding the block.
-
1
def display(line, column=0) #:yield:
-
result = "\e[s"
-
result << "\e[#{line.to_i};#{column.to_i}H"
-
if block_given?
-
result << yield
-
result << "\e[u"
-
#elsif string
-
# result << string
-
# result << "\e[u"
-
end
-
result
-
end
-
-
# Move cursor to line and column.
-
1
def move(line, column=0)
-
"\e[#{line.to_i};#{column.to_i}H"
-
end
-
-
# Move cursor up a specificed number of spaces.
-
1
def up(spaces=1)
-
"\e[#{spaces.to_i}A"
-
end
-
-
# Move cursor down a specificed number of spaces.
-
1
def down(spaces=1)
-
"\e[#{spaces.to_i}B"
-
end
-
-
# Move cursor left a specificed number of spaces.
-
1
def left(spaces=1)
-
"\e[#{spaces.to_i}D"
-
end
-
-
# Move cursor right a specificed number of spaces.
-
1
def right(spaces=1)
-
"\e[#{spaces.to_i}C"
-
end
-
-
##
-
#def position
-
# "\e[#;#R"
-
#end
-
-
# Apply ANSI codes to a first argument or block value.
-
#
-
# @example
-
# ansi("Valentine", :red, :on_white)
-
#
-
# @example
-
# ansi(:red, :on_white){ "Valentine" }
-
#
-
# @return [String]
-
# String wrapped ANSI code.
-
#
-
1
def ansi(*codes) #:yield:
-
if block_given?
-
string = yield.to_s
-
else
-
string = codes.shift.to_s
-
end
-
-
return string unless $ansi
-
-
c = code(*codes)
-
-
c + string.gsub(ENDCODE, ENDCODE + c) + ENDCODE
-
end
-
-
# TODO: Allow selective removal using *codes argument?
-
-
# Remove ANSI codes from string or block value.
-
#
-
# @param [String]
-
# String from which to remove ANSI codes.
-
#
-
# @return [String]
-
# String wrapped ANSI code.
-
#
-
1
def unansi(string=nil) #:yield:
-
if block_given?
-
string = yield.to_s
-
else
-
string = string.to_s
-
end
-
string.gsub(PATTERN, '')
-
end
-
-
# Alias for #ansi method.
-
#
-
# @deprecated
-
# Here for backward scompatibility.
-
1
alias_method :style, :ansi
-
-
# Alias for #unansi method.
-
#
-
# @deprecated
-
# Here for backwards compatibility.
-
1
alias_method :unstyle, :unansi
-
-
# Alternate term for #ansi.
-
#
-
# @deprecated
-
# May change in future definition.
-
1
alias_method :color, :ansi
-
-
# Alias for unansi.
-
#
-
# @deprecated
-
# May change in future definition.
-
1
alias_method :uncolor, :unansi
-
-
# Look-up code from chart, or if Integer simply pass through.
-
# Also resolves :random and :on_random.
-
#
-
# @param codes [Array<Symbol,Integer]
-
# Symbols or integers to covnert to ANSI code.
-
#
-
# @return [String] ANSI code
-
1
def code(*codes)
-
list = []
-
codes.each do |code|
-
list << \
-
case code
-
when Integer
-
code
-
when Array
-
rgb(*code)
-
when :random
-
random
-
when :on_random
-
random(true)
-
else
-
CHART[code.to_sym]
-
end
-
end
-
"\e[" + (list * ";") + "m"
-
end
-
-
# Provides a random primary ANSI color.
-
#
-
# @param background [Boolean]
-
# Use `true` for background color, otherwise foreground color.
-
#
-
# @return [Integer] ANSI color number
-
1
def random(background=false)
-
(background ? 40 : 30) + rand(8)
-
end
-
-
# Creates an xterm-256 color from rgb value.
-
#
-
# @param background [Boolean]
-
# Use `true` for background color, otherwise foreground color.
-
#
-
1
def rgb(red, green, blue, background=false)
-
"#{background ? 48 : 38};5;#{rgb_value(red, green, blue)}"
-
end
-
-
# Creates an xterm-256 color from a CSS-style color string.
-
1
def hex(string, background=false)
-
string.tr!('#','')
-
x = (string.size == 6 ? 2 : 1)
-
r, g, b = [0,1,2].map{ |i| string[i*x,2].to_i(16) }
-
rgb(r, g, b, background)
-
end
-
-
1
private
-
-
# Gets closest xterm-256 color.
-
1
def rgb_256(r, g, b)
-
r, g, b = [r, g, b].map{ |c| rgb_valid(c); (6 * (c.to_f / 256.0)).to_i }
-
v = (r * 36 + g * 6 + b + 16).abs
-
raise ArgumentError, "RGB value outside 0-255 range" if v > 255
-
v
-
end
-
-
end
-
-
#
-
1
extend Code
-
end
-
-
1
module ANSI
-
-
1
require 'ansi/chart'
-
-
# Converts {CHART} and {SPECIAL_CHART} entries into constants.
-
# So for example, the CHART entry for :red becomes:
-
#
-
# ANSI::Constants::RED #=> "\e[31m"
-
#
-
# The ANSI Constants are include into ANSI::Code and can be included
-
# any where will they would be of use.
-
#
-
1
module Constants
-
-
1
CHART.each do |name, code|
-
72
const_set(name.to_s.upcase, "\e[#{code}m")
-
end
-
-
1
SPECIAL_CHART.each do |name, code|
-
6
const_set(name.to_s.upcase, code)
-
end
-
-
end
-
-
end
-
1
module ANSI
-
# Returns Hash table of project metadata.
-
1
def self.metadata
-
@spec ||= (
-
require 'yaml'
-
YAML.load(File.new(File.dirname(__FILE__) + '/../ansi.yml'))
-
)
-
end
-
-
# Check metadata for missing constants.
-
1
def self.const_missing(name)
-
metadata[name.to_s.downcase] || super(name)
-
end
-
end
-
-
1
require 'arel/crud'
-
1
require 'arel/factory_methods'
-
-
1
require 'arel/expressions'
-
1
require 'arel/predications'
-
1
require 'arel/math'
-
1
require 'arel/alias_predication'
-
1
require 'arel/order_predications'
-
1
require 'arel/table'
-
1
require 'arel/attributes'
-
1
require 'arel/compatibility/wheres'
-
-
#### these are deprecated
-
# The Arel::Relation constant is referenced in Rails
-
1
require 'arel/relation'
-
1
require 'arel/expression'
-
####
-
-
1
require 'arel/visitors'
-
-
1
require 'arel/tree_manager'
-
1
require 'arel/insert_manager'
-
1
require 'arel/select_manager'
-
1
require 'arel/update_manager'
-
1
require 'arel/delete_manager'
-
1
require 'arel/nodes'
-
-
-
#### these are deprecated
-
1
require 'arel/deprecated'
-
1
require 'arel/sql/engine'
-
1
require 'arel/sql_literal'
-
####
-
-
1
module Arel
-
1
VERSION = '2.2.3'
-
-
1
def self.sql raw_sql
-
Arel::Nodes::SqlLiteral.new raw_sql
-
end
-
-
1
def self.star
-
sql '*'
-
end
-
## Convenience Alias
-
1
Node = Arel::Nodes::Node
-
end
-
1
module Arel
-
1
module AliasPredication
-
1
def as other
-
Nodes::As.new self, Nodes::SqlLiteral.new(other)
-
end
-
end
-
end
-
1
require 'arel/attributes/attribute'
-
-
1
module Arel
-
1
module Attributes
-
###
-
# Factory method to wrap a raw database +column+ to an Arel Attribute.
-
1
def self.for column
-
case column.type
-
when :string, :text, :binary then String
-
when :integer then Integer
-
when :float then Float
-
when :decimal then Decimal
-
when :date, :datetime, :timestamp, :time then Time
-
when :boolean then Boolean
-
else
-
Undefined
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Attributes
-
1
class Attribute < Struct.new :relation, :name
-
1
include Arel::Expressions
-
1
include Arel::Predications
-
1
include Arel::AliasPredication
-
1
include Arel::OrderPredications
-
1
include Arel::Math
-
-
###
-
# Create a node for lowering this attribute
-
1
def lower
-
relation.lower self
-
end
-
end
-
-
1
class String < Attribute; end
-
1
class Time < Attribute; end
-
1
class Boolean < Attribute; end
-
1
class Decimal < Attribute; end
-
1
class Float < Attribute; end
-
1
class Integer < Attribute; end
-
1
class Undefined < Attribute; end
-
end
-
-
1
Attribute = Attributes::Attribute
-
end
-
1
module Arel
-
1
module Compatibility # :nodoc:
-
1
class Wheres # :nodoc:
-
1
include Enumerable
-
-
1
module Value # :nodoc:
-
1
attr_accessor :visitor
-
1
def value
-
visitor.accept self
-
end
-
-
1
def name
-
super.to_sym
-
end
-
end
-
-
1
def initialize engine, collection
-
@engine = engine
-
@collection = collection
-
end
-
-
1
def each
-
to_sql = Visitors::ToSql.new @engine
-
-
@collection.each { |c|
-
c.extend(Value)
-
c.visitor = to_sql
-
yield c
-
}
-
end
-
end
-
end
-
end
-
1
module Arel
-
###
-
# FIXME hopefully we can remove this
-
1
module Crud
-
1
def compile_update values
-
um = UpdateManager.new @engine
-
-
if Nodes::SqlLiteral === values
-
relation = @ctx.from
-
else
-
relation = values.first.first.relation
-
end
-
um.table relation
-
um.set values
-
um.take @ast.limit.expr if @ast.limit
-
um.order(*@ast.orders)
-
um.wheres = @ctx.wheres
-
um
-
end
-
-
# FIXME: this method should go away
-
1
def update values
-
if $VERBOSE
-
warn <<-eowarn
-
update (#{caller.first}) is deprecated and will be removed in ARel 3.0.0. Please
-
switch to `compile_update`
-
eowarn
-
end
-
-
um = compile_update values
-
@engine.connection.update um.to_sql, 'AREL'
-
end
-
-
1
def compile_insert values
-
im = create_insert
-
im.insert values
-
im
-
end
-
-
1
def create_insert
-
InsertManager.new @engine
-
end
-
-
# FIXME: this method should go away
-
1
def insert values
-
if $VERBOSE
-
warn <<-eowarn
-
insert (#{caller.first}) is deprecated and will be removed in ARel 3.0.0. Please
-
switch to `compile_insert`
-
eowarn
-
end
-
@engine.connection.insert compile_insert(values).to_sql
-
end
-
-
1
def compile_delete
-
dm = DeleteManager.new @engine
-
dm.wheres = @ctx.wheres
-
dm.from @ctx.froms
-
dm
-
end
-
-
1
def delete
-
if $VERBOSE
-
warn <<-eowarn
-
delete (#{caller.first}) is deprecated and will be removed in ARel 3.0.0. Please
-
switch to `compile_delete`
-
eowarn
-
end
-
@engine.connection.delete compile_delete.to_sql, 'AREL'
-
end
-
end
-
end
-
1
module Arel
-
1
class DeleteManager < Arel::TreeManager
-
1
def initialize engine
-
super
-
@ast = Nodes::DeleteStatement.new
-
@ctx = @ast
-
end
-
-
1
def from relation
-
@ast.relation = relation
-
self
-
end
-
-
1
def wheres= list
-
@ast.wheres = list
-
end
-
end
-
end
-
1
module Arel
-
1
InnerJoin = Nodes::InnerJoin
-
1
OuterJoin = Nodes::OuterJoin
-
end
-
1
module Arel
-
1
module Expression
-
1
include Arel::OrderPredications
-
end
-
end
-
1
module Arel
-
1
module Expressions
-
1
def count distinct = false
-
Nodes::Count.new [self], distinct
-
end
-
-
1
def sum
-
Nodes::Sum.new [self], Nodes::SqlLiteral.new('sum_id')
-
end
-
-
1
def maximum
-
Nodes::Max.new [self], Nodes::SqlLiteral.new('max_id')
-
end
-
-
1
def minimum
-
Nodes::Min.new [self], Nodes::SqlLiteral.new('min_id')
-
end
-
-
1
def average
-
Nodes::Avg.new [self], Nodes::SqlLiteral.new('avg_id')
-
end
-
end
-
end
-
1
module Arel
-
###
-
# Methods for creating various nodes
-
1
module FactoryMethods
-
1
def create_true
-
Arel::Nodes::True.new
-
end
-
-
1
def create_false
-
Arel::Nodes::False.new
-
end
-
-
1
def create_table_alias relation, name
-
Nodes::TableAlias.new(relation, name)
-
end
-
-
1
def create_join to, constraint = nil, klass = Nodes::InnerJoin
-
klass.new(to, constraint)
-
end
-
-
1
def create_string_join to
-
create_join to, nil, Nodes::StringJoin
-
end
-
-
1
def create_and clauses
-
Nodes::And.new clauses
-
end
-
-
1
def create_on expr
-
Nodes::On.new expr
-
end
-
-
1
def grouping expr
-
Nodes::Grouping.new expr
-
end
-
-
###
-
# Create a LOWER() function
-
1
def lower column
-
Nodes::NamedFunction.new 'LOWER', [column]
-
end
-
end
-
end
-
1
module Arel
-
1
class InsertManager < Arel::TreeManager
-
1
def initialize engine
-
super
-
@ast = Nodes::InsertStatement.new
-
end
-
-
1
def into table
-
@ast.relation = table
-
self
-
end
-
-
1
def columns; @ast.columns end
-
1
def values= val; @ast.values = val; end
-
-
1
def insert fields
-
return if fields.empty?
-
-
if String === fields
-
@ast.values = SqlLiteral.new(fields)
-
else
-
@ast.relation ||= fields.first.first.relation
-
-
values = []
-
-
fields.each do |column, value|
-
@ast.columns << column
-
values << value
-
end
-
@ast.values = create_values values, @ast.columns
-
end
-
end
-
-
1
def create_values values, columns
-
Nodes::Values.new values, columns
-
end
-
end
-
end
-
1
module Arel
-
1
module Math
-
1
def *(other)
-
Arel::Nodes::Multiplication.new(self, other)
-
end
-
-
1
def +(other)
-
Arel::Nodes::Grouping.new(Arel::Nodes::Addition.new(self, other))
-
end
-
-
1
def -(other)
-
Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, other))
-
end
-
-
1
def /(other)
-
Arel::Nodes::Division.new(self, other)
-
end
-
end
-
end
-
# node
-
1
require 'arel/nodes/node'
-
1
require 'arel/nodes/select_statement'
-
1
require 'arel/nodes/select_core'
-
1
require 'arel/nodes/insert_statement'
-
1
require 'arel/nodes/update_statement'
-
-
# terminal
-
-
1
require 'arel/nodes/terminal'
-
1
require 'arel/nodes/true'
-
1
require 'arel/nodes/false'
-
-
# unary
-
1
require 'arel/nodes/unary'
-
1
require 'arel/nodes/ascending'
-
1
require 'arel/nodes/descending'
-
1
require 'arel/nodes/unqualified_column'
-
1
require 'arel/nodes/with'
-
-
# binary
-
1
require 'arel/nodes/binary'
-
1
require 'arel/nodes/equality'
-
1
require 'arel/nodes/in' # Why is this subclassed from equality?
-
1
require 'arel/nodes/join_source'
-
1
require 'arel/nodes/delete_statement'
-
1
require 'arel/nodes/table_alias'
-
1
require 'arel/nodes/infix_operation'
-
-
# nary
-
1
require 'arel/nodes/and'
-
-
# function
-
# FIXME: Function + Alias can be rewritten as a Function and Alias node.
-
# We should make Function a Unary node and deprecate the use of "aliaz"
-
1
require 'arel/nodes/function'
-
1
require 'arel/nodes/count'
-
1
require 'arel/nodes/values'
-
1
require 'arel/nodes/named_function'
-
-
# joins
-
1
require 'arel/nodes/inner_join'
-
1
require 'arel/nodes/outer_join'
-
1
require 'arel/nodes/string_join'
-
-
1
require 'arel/nodes/sql_literal'
-
1
module Arel
-
1
module Nodes
-
1
class And < Arel::Nodes::Node
-
1
attr_reader :children
-
-
1
def initialize children, right = nil
-
unless Array === children
-
warn "(#{caller.first}) AND nodes should be created with a list"
-
children = [children, right]
-
end
-
@children = children
-
end
-
-
1
def left
-
children.first
-
end
-
-
1
def right
-
children[1]
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class Ascending < Ordering
-
-
1
def reverse
-
Descending.new(expr)
-
end
-
-
1
def direction
-
:asc
-
end
-
-
1
def ascending?
-
true
-
end
-
-
1
def descending?
-
false
-
end
-
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class Binary < Arel::Nodes::Node
-
1
attr_accessor :left, :right
-
-
1
def initialize left, right
-
@left = left
-
@right = right
-
end
-
-
1
def initialize_copy other
-
super
-
@left = @left.clone if @left
-
@right = @right.clone if @right
-
end
-
end
-
-
%w{
-
As
-
1
Assignment
-
Between
-
DoesNotMatch
-
GreaterThan
-
GreaterThanOrEqual
-
Join
-
LessThan
-
LessThanOrEqual
-
Matches
-
NotEqual
-
NotIn
-
Or
-
Union
-
UnionAll
-
Intersect
-
Except
-
}.each do |name|
-
17
const_set name, Class.new(Binary)
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class Count < Arel::Nodes::Function
-
1
def initialize expr, distinct = false, aliaz = nil
-
super(expr, aliaz)
-
@distinct = distinct
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class DeleteStatement < Arel::Nodes::Binary
-
1
alias :relation :left
-
1
alias :relation= :left=
-
1
alias :wheres :right
-
1
alias :wheres= :right=
-
-
1
def initialize relation = nil, wheres = []
-
super
-
end
-
-
1
def initialize_copy other
-
super
-
@right = @right.clone
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class Descending < Ordering
-
-
1
def reverse
-
Ascending.new(expr)
-
end
-
-
1
def direction
-
:desc
-
end
-
-
1
def ascending?
-
false
-
end
-
-
1
def descending?
-
true
-
end
-
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class Equality < Arel::Nodes::Binary
-
1
def operator; :== end
-
1
alias :operand1 :left
-
1
alias :operand2 :right
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class False < Arel::Nodes::Node
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class Function < Arel::Nodes::Node
-
1
include Arel::Expression
-
1
include Arel::Predications
-
1
attr_accessor :expressions, :alias, :distinct
-
-
1
def initialize expr, aliaz = nil
-
@expressions = expr
-
@alias = aliaz && SqlLiteral.new(aliaz)
-
@distinct = false
-
end
-
-
1
def as aliaz
-
self.alias = SqlLiteral.new(aliaz)
-
self
-
end
-
end
-
-
%w{
-
Sum
-
1
Exists
-
Max
-
Min
-
Avg
-
}.each do |name|
-
5
const_set(name, Class.new(Function))
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class In < Equality
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
-
1
class InfixOperation < Binary
-
1
include Arel::Expressions
-
1
include Arel::Predications
-
1
include Arel::OrderPredications
-
1
include Arel::AliasPredication
-
1
include Arel::Math
-
-
1
attr_reader :operator
-
-
1
def initialize operator, left, right
-
super(left, right)
-
@operator = operator
-
end
-
end
-
-
1
class Multiplication < InfixOperation
-
1
def initialize left, right
-
super(:*, left, right)
-
end
-
end
-
-
1
class Division < InfixOperation
-
1
def initialize left, right
-
super(:/, left, right)
-
end
-
end
-
-
1
class Addition < InfixOperation
-
1
def initialize left, right
-
super(:+, left, right)
-
end
-
end
-
-
1
class Subtraction < InfixOperation
-
1
def initialize left, right
-
super(:-, left, right)
-
end
-
end
-
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class InnerJoin < Arel::Nodes::Join
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class InsertStatement < Arel::Nodes::Node
-
1
attr_accessor :relation, :columns, :values
-
-
1
def initialize
-
@relation = nil
-
@columns = []
-
@values = nil
-
end
-
-
1
def initialize_copy other
-
super
-
@columns = @columns.clone
-
@values = @values.clone if @values
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
###
-
# Class that represents a join source
-
#
-
# http://www.sqlite.org/syntaxdiagrams.html#join-source
-
-
1
class JoinSource < Arel::Nodes::Binary
-
1
def initialize single_source, joinop = []
-
super
-
end
-
-
1
def empty?
-
!left && right.empty?
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class NamedFunction < Arel::Nodes::Function
-
1
attr_accessor :name
-
-
1
def initialize name, expr, aliaz = nil
-
super(expr, aliaz)
-
@name = name
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
###
-
# Abstract base class for all AST nodes
-
1
class Node
-
1
include Arel::FactoryMethods
-
1
include Enumerable
-
-
###
-
# Factory method to create a Nodes::Not node that has the recipient of
-
# the caller as a child.
-
1
def not
-
Nodes::Not.new self
-
end
-
-
###
-
# Factory method to create a Nodes::Grouping node that has an Nodes::Or
-
# node as a child.
-
1
def or right
-
Nodes::Grouping.new Nodes::Or.new(self, right)
-
end
-
-
###
-
# Factory method to create an Nodes::And node.
-
1
def and right
-
Nodes::And.new [self, right]
-
end
-
-
# FIXME: this method should go away. I don't like people calling
-
# to_sql on non-head nodes. This forces us to walk the AST until we
-
# can find a node that has a "relation" member.
-
#
-
# Maybe we should just use `Table.engine`? :'(
-
1
def to_sql engine = Table.engine
-
engine.connection.visitor.accept self
-
end
-
-
# Iterate through AST, nodes will be yielded depth-first
-
1
def each &block
-
return enum_for(:each) unless block_given?
-
-
::Arel::Visitors::DepthFirst.new(block).accept self
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class OuterJoin < Arel::Nodes::Join
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class SelectCore < Arel::Nodes::Node
-
1
attr_accessor :top, :projections, :wheres, :groups
-
1
attr_accessor :having, :source, :set_quantifier
-
-
1
def initialize
-
@source = JoinSource.new nil
-
@top = nil
-
-
# http://savage.net.au/SQL/sql-92.bnf.html#set%20quantifier
-
@set_quantifier = nil
-
@projections = []
-
@wheres = []
-
@groups = []
-
@having = nil
-
end
-
-
1
def from
-
@source.left
-
end
-
-
1
def from= value
-
@source.left = value
-
end
-
-
1
alias :froms= :from=
-
1
alias :froms :from
-
-
1
def initialize_copy other
-
super
-
@source = @source.clone if @source
-
@projections = @projections.clone
-
@wheres = @wheres.clone
-
@groups = @groups.clone
-
@having = @having.clone if @having
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class SelectStatement < Arel::Nodes::Node
-
1
attr_reader :cores
-
1
attr_accessor :limit, :orders, :lock, :offset, :with
-
-
1
def initialize cores = [SelectCore.new]
-
#puts caller
-
@cores = cores
-
@orders = []
-
@limit = nil
-
@lock = nil
-
@offset = nil
-
@with = nil
-
end
-
-
1
def initialize_copy other
-
super
-
@cores = @cores.map { |x| x.clone }
-
@orders = @orders.map { |x| x.clone }
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class SqlLiteral < String
-
1
include Arel::Expressions
-
1
include Arel::Predications
-
1
include Arel::AliasPredication
-
1
include Arel::OrderPredications
-
end
-
-
1
class BindParam < SqlLiteral
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class StringJoin < Arel::Nodes::Join
-
1
def initialize left, right = nil
-
super
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class TableAlias < Arel::Nodes::Binary
-
1
alias :name :right
-
1
alias :relation :left
-
1
alias :table_alias :name
-
-
1
def [] name
-
Attribute.new(self, name)
-
end
-
-
1
def table_name
-
relation.respond_to?(:name) ? relation.name : name
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class Distinct < Arel::Nodes::Node
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class True < Arel::Nodes::Node
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class Unary < Arel::Nodes::Node
-
1
attr_accessor :expr
-
1
alias :value :expr
-
-
1
def initialize expr
-
@expr = expr
-
end
-
end
-
-
%w{
-
Bin
-
1
Group
-
Grouping
-
Having
-
Limit
-
Not
-
Offset
-
On
-
Ordering
-
Top
-
Lock
-
DistinctOn
-
}.each do |name|
-
12
const_set(name, Class.new(Unary))
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class UnqualifiedColumn < Arel::Nodes::Unary
-
1
alias :attribute :expr
-
1
alias :attribute= :expr=
-
-
1
def relation
-
@expr.relation
-
end
-
-
1
def column
-
@expr.column
-
end
-
-
1
def name
-
@expr.name
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class UpdateStatement < Arel::Nodes::Node
-
1
attr_accessor :relation, :wheres, :values, :orders, :limit
-
1
attr_accessor :key
-
-
1
def initialize
-
@relation = nil
-
@wheres = []
-
@values = []
-
@orders = []
-
@limit = nil
-
@key = nil
-
end
-
-
1
def initialize_copy other
-
super
-
@wheres = @wheres.clone
-
@values = @values.clone
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class Values < Arel::Nodes::Binary
-
1
alias :expressions :left
-
1
alias :expressions= :left=
-
1
alias :columns :right
-
1
alias :columns= :right=
-
-
1
def initialize exprs, columns = []
-
super
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class With < Arel::Nodes::Unary
-
1
alias children expr
-
end
-
-
1
class WithRecursive < With; end
-
end
-
end
-
-
1
module Arel
-
1
module OrderPredications
-
-
1
def asc
-
Nodes::Ascending.new self
-
end
-
-
1
def desc
-
Nodes::Descending.new self
-
end
-
-
end
-
end
-
1
module Arel
-
1
module Predications
-
1
def not_eq other
-
Nodes::NotEqual.new self, other
-
end
-
-
1
def not_eq_any others
-
grouping_any :not_eq, others
-
end
-
-
1
def not_eq_all others
-
grouping_all :not_eq, others
-
end
-
-
1
def eq other
-
Nodes::Equality.new self, other
-
end
-
-
1
def eq_any others
-
grouping_any :eq, others
-
end
-
-
1
def eq_all others
-
grouping_all :eq, others
-
end
-
-
1
def in other
-
case other
-
when Arel::SelectManager
-
Arel::Nodes::In.new(self, other.ast)
-
when Range
-
if other.exclude_end?
-
left = Nodes::GreaterThanOrEqual.new(self, other.begin)
-
right = Nodes::LessThan.new(self, other.end)
-
Nodes::And.new [left, right]
-
else
-
Nodes::Between.new(self, Nodes::And.new([other.begin, other.end]))
-
end
-
else
-
Nodes::In.new self, other
-
end
-
end
-
-
1
def in_any others
-
grouping_any :in, others
-
end
-
-
1
def in_all others
-
grouping_all :in, others
-
end
-
-
1
def not_in other
-
case other
-
when Arel::SelectManager
-
Arel::Nodes::NotIn.new(self, other.ast)
-
when Range
-
if other.exclude_end?
-
left = Nodes::LessThan.new(self, other.begin)
-
right = Nodes::GreaterThanOrEqual.new(self, other.end)
-
Nodes::Or.new left, right
-
else
-
left = Nodes::LessThan.new(self, other.begin)
-
right = Nodes::GreaterThan.new(self, other.end)
-
Nodes::Or.new left, right
-
end
-
else
-
Nodes::NotIn.new self, other
-
end
-
end
-
-
1
def not_in_any others
-
grouping_any :not_in, others
-
end
-
-
1
def not_in_all others
-
grouping_all :not_in, others
-
end
-
-
1
def matches other
-
Nodes::Matches.new self, other
-
end
-
-
1
def matches_any others
-
grouping_any :matches, others
-
end
-
-
1
def matches_all others
-
grouping_all :matches, others
-
end
-
-
1
def does_not_match other
-
Nodes::DoesNotMatch.new self, other
-
end
-
-
1
def does_not_match_any others
-
grouping_any :does_not_match, others
-
end
-
-
1
def does_not_match_all others
-
grouping_all :does_not_match, others
-
end
-
-
1
def gteq right
-
Nodes::GreaterThanOrEqual.new self, right
-
end
-
-
1
def gteq_any others
-
grouping_any :gteq, others
-
end
-
-
1
def gteq_all others
-
grouping_all :gteq, others
-
end
-
-
1
def gt right
-
Nodes::GreaterThan.new self, right
-
end
-
-
1
def gt_any others
-
grouping_any :gt, others
-
end
-
-
1
def gt_all others
-
grouping_all :gt, others
-
end
-
-
1
def lt right
-
Nodes::LessThan.new self, right
-
end
-
-
1
def lt_any others
-
grouping_any :lt, others
-
end
-
-
1
def lt_all others
-
grouping_all :lt, others
-
end
-
-
1
def lteq right
-
Nodes::LessThanOrEqual.new self, right
-
end
-
-
1
def lteq_any others
-
grouping_any :lteq, others
-
end
-
-
1
def lteq_all others
-
grouping_all :lteq, others
-
end
-
-
1
private
-
-
1
def grouping_any method_id, others
-
nodes = others.map {|expr| send(method_id, expr)}
-
Nodes::Grouping.new nodes.inject { |memo,node|
-
Nodes::Or.new(memo, node)
-
}
-
end
-
-
1
def grouping_all method_id, others
-
Nodes::Grouping.new Nodes::And.new(others.map {|expr| send(method_id, expr)})
-
end
-
end
-
end
-
1
module Arel
-
###
-
# This is deprecated. Fix rails, then remove this.
-
1
module Relation
-
end
-
end
-
1
module Arel
-
1
class SelectManager < Arel::TreeManager
-
1
include Arel::Crud
-
-
1
def initialize engine, table = nil
-
super(engine)
-
@ast = Nodes::SelectStatement.new
-
@ctx = @ast.cores.last
-
from table
-
end
-
-
1
def initialize_copy other
-
super
-
@ctx = @ast.cores.last
-
end
-
-
1
def limit
-
@ast.limit && @ast.limit.expr
-
end
-
1
alias :taken :limit
-
-
1
def constraints
-
@ctx.wheres
-
end
-
-
1
def offset
-
@ast.offset && @ast.offset.expr
-
end
-
-
1
def skip amount
-
if amount
-
@ast.offset = Nodes::Offset.new(amount)
-
else
-
@ast.offset = nil
-
end
-
self
-
end
-
1
alias :offset= :skip
-
-
###
-
# Produces an Arel::Nodes::Exists node
-
1
def exists
-
Arel::Nodes::Exists.new @ast
-
end
-
-
1
def as other
-
create_table_alias grouping(@ast), Nodes::SqlLiteral.new(other)
-
end
-
-
1
def where_clauses
-
if $VERBOSE
-
warn "(#{caller.first}) where_clauses is deprecated and will be removed in arel 3.0.0 with no replacement"
-
end
-
to_sql = Visitors::ToSql.new @engine.connection_pool
-
@ctx.wheres.map { |c| to_sql.accept c }
-
end
-
-
1
def lock locking = Arel.sql('FOR UPDATE')
-
case locking
-
when true
-
locking = Arel.sql('FOR UPDATE')
-
when Arel::Nodes::SqlLiteral
-
when String
-
locking = Arel.sql locking
-
end
-
-
@ast.lock = Nodes::Lock.new(locking)
-
self
-
end
-
-
1
def locked
-
@ast.lock
-
end
-
-
1
def on *exprs
-
@ctx.source.right.last.right = Nodes::On.new(collapse(exprs))
-
self
-
end
-
-
1
def group *columns
-
columns.each do |column|
-
# FIXME: backwards compat
-
column = Nodes::SqlLiteral.new(column) if String === column
-
column = Nodes::SqlLiteral.new(column.to_s) if Symbol === column
-
-
@ctx.groups.push Nodes::Group.new column
-
end
-
self
-
end
-
-
1
def from table
-
table = Nodes::SqlLiteral.new(table) if String === table
-
# FIXME: this is a hack to support
-
# test_with_two_tables_in_from_without_getting_double_quoted
-
# from the AR tests.
-
-
case table
-
when Nodes::Join
-
@ctx.source.right << table
-
else
-
@ctx.source.left = table
-
end
-
-
self
-
end
-
-
1
def froms
-
@ast.cores.map { |x| x.from }.compact
-
end
-
-
1
def join relation, klass = Nodes::InnerJoin
-
return self unless relation
-
-
case relation
-
when String, Nodes::SqlLiteral
-
raise if relation.blank?
-
klass = Nodes::StringJoin
-
end
-
-
@ctx.source.right << create_join(relation, nil, klass)
-
self
-
end
-
-
1
def having *exprs
-
@ctx.having = Nodes::Having.new(collapse(exprs, @ctx.having))
-
self
-
end
-
-
1
def project *projections
-
# FIXME: converting these to SQLLiterals is probably not good, but
-
# rails tests require it.
-
@ctx.projections.concat projections.map { |x|
-
[Symbol, String].include?(x.class) ? SqlLiteral.new(x.to_s) : x
-
}
-
self
-
end
-
-
1
def projections= projections
-
@ctx.projections = projections
-
end
-
-
1
def distinct(value = true)
-
if value
-
@ctx.set_quantifier = Arel::Nodes::Distinct.new
-
else
-
@ctx.set_quantifier = nil
-
end
-
end
-
-
1
def order *expr
-
# FIXME: We SHOULD NOT be converting these to SqlLiteral automatically
-
@ast.orders.concat expr.map { |x|
-
String === x || Symbol === x ? Nodes::SqlLiteral.new(x.to_s) : x
-
}
-
self
-
end
-
-
1
def orders
-
@ast.orders
-
end
-
-
1
def wheres
-
warn "#{caller[0]}: SelectManager#wheres is deprecated and will be removed in ARel 3.0.0 with no replacement"
-
Compatibility::Wheres.new @engine.connection_pool, @ctx.wheres
-
end
-
-
1
def where_sql
-
return if @ctx.wheres.empty?
-
-
viz = Visitors::WhereSql.new @engine.connection_pool
-
Nodes::SqlLiteral.new viz.accept @ctx
-
end
-
-
1
def union operation, other = nil
-
if other
-
node_class = Nodes.const_get("Union#{operation.to_s.capitalize}")
-
else
-
other = operation
-
node_class = Nodes::Union
-
end
-
-
node_class.new self.ast, other.ast
-
end
-
-
1
def intersect other
-
Nodes::Intersect.new ast, other.ast
-
end
-
-
1
def except other
-
Nodes::Except.new ast, other.ast
-
end
-
1
alias :minus :except
-
-
1
def with *subqueries
-
if subqueries.first.is_a? Symbol
-
node_class = Nodes.const_get("With#{subqueries.shift.to_s.capitalize}")
-
else
-
node_class = Nodes::With
-
end
-
@ast.with = node_class.new(subqueries.flatten)
-
-
self
-
end
-
-
1
def take limit
-
if limit
-
@ast.limit = Nodes::Limit.new(limit)
-
@ctx.top = Nodes::Top.new(limit)
-
else
-
@ast.limit = nil
-
@ctx.top = nil
-
end
-
self
-
end
-
1
alias limit= take
-
-
1
def join_sql
-
return nil if @ctx.source.right.empty?
-
-
sql = visitor.dup.extend(Visitors::JoinSql).accept @ctx
-
Nodes::SqlLiteral.new sql
-
end
-
-
1
def order_clauses
-
visitor = Visitors::OrderClauses.new(@engine.connection_pool)
-
visitor.accept(@ast).map { |x|
-
Nodes::SqlLiteral.new x
-
}
-
end
-
-
1
def join_sources
-
@ctx.source.right
-
end
-
-
1
def source
-
@ctx.source
-
end
-
-
1
def joins manager
-
if $VERBOSE
-
warn "joins is deprecated and will be removed in 3.0.0"
-
warn "please remove your call to joins from #{caller.first}"
-
end
-
manager.join_sql
-
end
-
-
1
class Row < Struct.new(:data) # :nodoc:
-
1
def id
-
data['id']
-
end
-
-
1
def method_missing(name, *args)
-
name = name.to_s
-
return data[name] if data.key?(name)
-
super
-
end
-
end
-
-
1
def to_a # :nodoc:
-
warn "to_a is deprecated. Please remove it from #{caller[0]}"
-
# FIXME: I think `select` should be made public...
-
@engine.connection.send(:select, to_sql, 'AREL').map { |x| Row.new(x) }
-
end
-
-
# FIXME: this method should go away
-
1
def insert values
-
if $VERBOSE
-
warn <<-eowarn
-
insert (#{caller.first}) is deprecated and will be removed in ARel 3.0.0. Please
-
switch to `compile_insert`
-
eowarn
-
end
-
-
im = compile_insert(values)
-
table = @ctx.froms
-
-
primary_key = table.primary_key
-
primary_key_name = primary_key.name if primary_key
-
-
# FIXME: in AR tests values sometimes were Array and not Hash therefore is_a?(Hash) check is added
-
primary_key_value = primary_key && values.is_a?(Hash) && values[primary_key]
-
im.into table
-
# Oracle adapter needs primary key name to generate RETURNING ... INTO ... clause
-
# for tables which assign primary key value using trigger.
-
# RETURNING ... INTO ... clause will be added only if primary_key_value is nil
-
# therefore it is necessary to pass primary key value as well
-
@engine.connection.insert im.to_sql, 'AREL', primary_key_name, primary_key_value
-
end
-
-
1
private
-
1
def collapse exprs, existing = nil
-
exprs = exprs.unshift(existing.expr) if existing
-
exprs = exprs.compact.map { |expr|
-
if String === expr
-
# FIXME: Don't do this automatically
-
Arel.sql(expr)
-
else
-
expr
-
end
-
}
-
-
if exprs.length == 1
-
exprs.first
-
else
-
create_and exprs
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Sql
-
1
class Engine
-
1
def self.new thing
-
#warn "#{caller.first} -- Engine will be removed"
-
thing
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
class SqlLiteral < Nodes::SqlLiteral
-
end
-
end
-
1
module Arel
-
1
class Table
-
1
include Arel::Crud
-
1
include Arel::FactoryMethods
-
-
1
@engine = nil
-
2
class << self; attr_accessor :engine; end
-
-
1
attr_accessor :name, :engine, :aliases, :table_alias
-
-
# TableAlias and Table both have a #table_name which is the name of the underlying table
-
1
alias :table_name :name
-
-
1
def initialize name, engine = Table.engine
-
2
@name = name.to_s
-
2
@engine = engine
-
2
@columns = nil
-
2
@aliases = []
-
2
@table_alias = nil
-
2
@primary_key = nil
-
-
2
if Hash === engine
-
@engine = engine[:engine] || Table.engine
-
-
# Sometime AR sends an :as parameter to table, to let the table know
-
# that it is an Alias. We may want to override new, and return a
-
# TableAlias node?
-
@table_alias = engine[:as] unless engine[:as].to_s == @name
-
end
-
end
-
-
1
def primary_key
-
if $VERBOSE
-
warn <<-eowarn
-
primary_key (#{caller.first}) is deprecated and will be removed in ARel 3.0.0
-
eowarn
-
end
-
@primary_key ||= begin
-
primary_key_name = @engine.connection.primary_key(name)
-
# some tables might be without primary key
-
primary_key_name && self[primary_key_name]
-
end
-
end
-
-
1
def alias name = "#{self.name}_2"
-
Nodes::TableAlias.new(self, name).tap do |node|
-
@aliases << node
-
end
-
end
-
-
1
def from table
-
SelectManager.new(@engine, table)
-
end
-
-
1
def joins manager
-
if $VERBOSE
-
warn "joins is deprecated and will be removed in 3.0.0"
-
warn "please remove your call to joins from #{caller.first}"
-
end
-
nil
-
end
-
-
1
def join relation, klass = Nodes::InnerJoin
-
return from(self) unless relation
-
-
case relation
-
when String, Nodes::SqlLiteral
-
raise if relation.blank?
-
klass = Nodes::StringJoin
-
end
-
-
from(self).join(relation, klass)
-
end
-
-
1
def group *columns
-
from(self).group(*columns)
-
end
-
-
1
def order *expr
-
from(self).order(*expr)
-
end
-
-
1
def where condition
-
from(self).where condition
-
end
-
-
1
def project *things
-
from(self).project(*things)
-
end
-
-
1
def take amount
-
from(self).take amount
-
end
-
-
1
def skip amount
-
from(self).skip amount
-
end
-
-
1
def having expr
-
from(self).having expr
-
end
-
-
1
def columns
-
if $VERBOSE
-
warn <<-eowarn
-
(#{caller.first}) Arel::Table#columns is deprecated and will be removed in
-
Arel 3.0.0 with no replacement. PEW PEW PEW!!!
-
eowarn
-
end
-
@columns ||=
-
attributes_for @engine.connection.columns(@name, "#{@name} Columns")
-
end
-
-
1
def [] name
-
::Arel::Attribute.new self, name
-
end
-
-
1
def select_manager
-
SelectManager.new(@engine)
-
end
-
-
1
def insert_manager
-
InsertManager.new(@engine)
-
end
-
-
1
private
-
-
1
def attributes_for columns
-
return nil unless columns
-
-
columns.map do |column|
-
Attributes.for(column).new self, column.name.to_sym
-
end
-
end
-
-
1
@@table_cache = nil
-
1
def self.table_cache engine # :nodoc:
-
if $VERBOSE
-
warn <<-eowarn
-
(#{caller.first}) Arel::Table.table_cache is deprecated and will be removed in
-
Arel 3.0.0 with no replacement. PEW PEW PEW!!!
-
eowarn
-
end
-
@@table_cache ||= Hash[engine.connection.tables.map { |x| [x,true] }]
-
end
-
end
-
end
-
1
module Arel
-
1
class TreeManager
-
# FIXME: Remove this.
-
1
include Arel::Relation
-
1
include Arel::FactoryMethods
-
-
1
attr_reader :ast, :engine
-
-
1
def initialize engine
-
@engine = engine
-
@ctx = nil
-
end
-
-
1
def to_dot
-
Visitors::Dot.new.accept @ast
-
end
-
-
1
def visitor
-
engine.connection.visitor
-
end
-
-
1
def to_sql
-
visitor.accept @ast
-
end
-
-
1
def initialize_copy other
-
super
-
@ast = @ast.clone
-
end
-
-
1
def where expr
-
if Arel::TreeManager === expr
-
expr = expr.ast
-
end
-
@ctx.wheres << expr
-
self
-
end
-
end
-
end
-
1
module Arel
-
1
class UpdateManager < Arel::TreeManager
-
1
def initialize engine
-
super
-
@ast = Nodes::UpdateStatement.new
-
@ctx = @ast
-
end
-
-
1
def take limit
-
@ast.limit = Nodes::Limit.new(limit) if limit
-
self
-
end
-
-
1
def key= key
-
@ast.key = key
-
end
-
-
1
def key
-
@ast.key
-
end
-
-
1
def order *expr
-
@ast.orders = expr
-
self
-
end
-
-
###
-
# UPDATE +table+
-
1
def table table
-
@ast.relation = table
-
self
-
end
-
-
1
def wheres= exprs
-
@ast.wheres = exprs
-
end
-
-
1
def where expr
-
@ast.wheres << expr
-
self
-
end
-
-
1
def set values
-
if String === values
-
@ast.values = [values]
-
else
-
@ast.values = values.map { |column,value|
-
Nodes::Assignment.new(
-
Nodes::UnqualifiedColumn.new(column),
-
value
-
)
-
}
-
end
-
self
-
end
-
end
-
end
-
1
require 'arel/visitors/visitor'
-
1
require 'arel/visitors/depth_first'
-
1
require 'arel/visitors/to_sql'
-
1
require 'arel/visitors/sqlite'
-
1
require 'arel/visitors/postgresql'
-
1
require 'arel/visitors/mysql'
-
1
require 'arel/visitors/mssql'
-
1
require 'arel/visitors/oracle'
-
1
require 'arel/visitors/join_sql'
-
1
require 'arel/visitors/where_sql'
-
1
require 'arel/visitors/order_clauses'
-
1
require 'arel/visitors/dot'
-
1
require 'arel/visitors/ibm_db'
-
1
require 'arel/visitors/informix'
-
-
1
module Arel
-
1
module Visitors
-
1
VISITORS = {
-
'postgresql' => Arel::Visitors::PostgreSQL,
-
'mysql' => Arel::Visitors::MySQL,
-
'mysql2' => Arel::Visitors::MySQL,
-
'mssql' => Arel::Visitors::MSSQL,
-
'sqlserver' => Arel::Visitors::MSSQL,
-
'oracle_enhanced' => Arel::Visitors::Oracle,
-
'sqlite' => Arel::Visitors::SQLite,
-
'sqlite3' => Arel::Visitors::SQLite,
-
'ibm_db' => Arel::Visitors::IBM_DB,
-
'informix' => Arel::Visitors::Informix,
-
}
-
-
1
ENGINE_VISITORS = Hash.new do |hash, engine|
-
pool = engine.connection_pool
-
adapter = pool.spec.config[:adapter]
-
hash[engine] = (VISITORS[adapter] || Visitors::ToSql).new(engine)
-
end
-
-
1
def self.visitor_for engine
-
ENGINE_VISITORS[engine]
-
end
-
2
class << self; alias :for :visitor_for; end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
class DepthFirst < Arel::Visitors::Visitor
-
1
def initialize block = nil
-
@block = block || Proc.new
-
end
-
-
1
private
-
-
1
def visit o
-
super
-
@block.call o
-
end
-
-
1
def unary o
-
visit o.expr
-
end
-
1
alias :visit_Arel_Nodes_Group :unary
-
1
alias :visit_Arel_Nodes_Grouping :unary
-
1
alias :visit_Arel_Nodes_Having :unary
-
1
alias :visit_Arel_Nodes_Limit :unary
-
1
alias :visit_Arel_Nodes_Not :unary
-
1
alias :visit_Arel_Nodes_Offset :unary
-
1
alias :visit_Arel_Nodes_On :unary
-
1
alias :visit_Arel_Nodes_Ordering :unary
-
1
alias :visit_Arel_Nodes_Ascending :unary
-
1
alias :visit_Arel_Nodes_Descending :unary
-
1
alias :visit_Arel_Nodes_Top :unary
-
1
alias :visit_Arel_Nodes_UnqualifiedColumn :unary
-
-
1
def function o
-
visit o.expressions
-
visit o.alias
-
visit o.distinct
-
end
-
1
alias :visit_Arel_Nodes_Avg :function
-
1
alias :visit_Arel_Nodes_Exists :function
-
1
alias :visit_Arel_Nodes_Max :function
-
1
alias :visit_Arel_Nodes_Min :function
-
1
alias :visit_Arel_Nodes_Sum :function
-
-
1
def visit_Arel_Nodes_NamedFunction o
-
visit o.name
-
visit o.expressions
-
visit o.distinct
-
visit o.alias
-
end
-
-
1
def visit_Arel_Nodes_Count o
-
visit o.expressions
-
visit o.alias
-
visit o.distinct
-
end
-
-
1
def nary o
-
o.children.each { |child| visit child }
-
end
-
1
alias :visit_Arel_Nodes_And :nary
-
-
1
def binary o
-
visit o.left
-
visit o.right
-
end
-
1
alias :visit_Arel_Nodes_As :binary
-
1
alias :visit_Arel_Nodes_Assignment :binary
-
1
alias :visit_Arel_Nodes_Between :binary
-
1
alias :visit_Arel_Nodes_DeleteStatement :binary
-
1
alias :visit_Arel_Nodes_DoesNotMatch :binary
-
1
alias :visit_Arel_Nodes_Equality :binary
-
1
alias :visit_Arel_Nodes_GreaterThan :binary
-
1
alias :visit_Arel_Nodes_GreaterThanOrEqual :binary
-
1
alias :visit_Arel_Nodes_In :binary
-
1
alias :visit_Arel_Nodes_JoinSource :binary
-
1
alias :visit_Arel_Nodes_InnerJoin :binary
-
1
alias :visit_Arel_Nodes_LessThan :binary
-
1
alias :visit_Arel_Nodes_LessThanOrEqual :binary
-
1
alias :visit_Arel_Nodes_Matches :binary
-
1
alias :visit_Arel_Nodes_NotEqual :binary
-
1
alias :visit_Arel_Nodes_NotIn :binary
-
1
alias :visit_Arel_Nodes_Or :binary
-
1
alias :visit_Arel_Nodes_OuterJoin :binary
-
1
alias :visit_Arel_Nodes_TableAlias :binary
-
1
alias :visit_Arel_Nodes_Values :binary
-
-
1
def visit_Arel_Nodes_StringJoin o
-
visit o.left
-
end
-
-
1
def visit_Arel_Attribute o
-
visit o.relation
-
visit o.name
-
end
-
1
alias :visit_Arel_Attributes_Integer :visit_Arel_Attribute
-
1
alias :visit_Arel_Attributes_Float :visit_Arel_Attribute
-
1
alias :visit_Arel_Attributes_String :visit_Arel_Attribute
-
1
alias :visit_Arel_Attributes_Time :visit_Arel_Attribute
-
1
alias :visit_Arel_Attributes_Boolean :visit_Arel_Attribute
-
1
alias :visit_Arel_Attributes_Attribute :visit_Arel_Attribute
-
1
alias :visit_Arel_Attributes_Decimal :visit_Arel_Attribute
-
-
1
def visit_Arel_Table o
-
visit o.name
-
end
-
-
1
def terminal o
-
end
-
1
alias :visit_ActiveSupport_Multibyte_Chars :terminal
-
1
alias :visit_ActiveSupport_StringInquirer :terminal
-
1
alias :visit_Arel_Nodes_Lock :terminal
-
1
alias :visit_Arel_Nodes_Node :terminal
-
1
alias :visit_Arel_Nodes_SqlLiteral :terminal
-
1
alias :visit_Arel_Nodes_BindParam :terminal
-
1
alias :visit_Arel_SqlLiteral :terminal
-
1
alias :visit_BigDecimal :terminal
-
1
alias :visit_Bignum :terminal
-
1
alias :visit_Class :terminal
-
1
alias :visit_Date :terminal
-
1
alias :visit_DateTime :terminal
-
1
alias :visit_FalseClass :terminal
-
1
alias :visit_Fixnum :terminal
-
1
alias :visit_Float :terminal
-
1
alias :visit_NilClass :terminal
-
1
alias :visit_String :terminal
-
1
alias :visit_Symbol :terminal
-
1
alias :visit_Time :terminal
-
1
alias :visit_TrueClass :terminal
-
-
1
def visit_Arel_Nodes_InsertStatement o
-
visit o.relation
-
visit o.columns
-
visit o.values
-
end
-
-
1
def visit_Arel_Nodes_SelectCore o
-
visit o.projections
-
visit o.source
-
visit o.wheres
-
visit o.groups
-
visit o.having
-
end
-
-
1
def visit_Arel_Nodes_SelectStatement o
-
visit o.cores
-
visit o.orders
-
visit o.limit
-
visit o.lock
-
visit o.offset
-
end
-
-
1
def visit_Arel_Nodes_UpdateStatement o
-
visit o.relation
-
visit o.values
-
visit o.wheres
-
visit o.orders
-
visit o.limit
-
end
-
-
1
def visit_Array o
-
o.each { |i| visit i }
-
end
-
-
1
def visit_Hash o
-
o.each { |k,v| visit(k); visit(v) }
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
class Dot < Arel::Visitors::Visitor
-
1
class Node # :nodoc:
-
1
attr_accessor :name, :id, :fields
-
-
1
def initialize name, id, fields = []
-
@name = name
-
@id = id
-
@fields = fields
-
end
-
end
-
-
1
class Edge < Struct.new :name, :from, :to # :nodoc:
-
end
-
-
1
def initialize
-
@nodes = []
-
@edges = []
-
@node_stack = []
-
@edge_stack = []
-
@seen = {}
-
end
-
-
1
def accept object
-
super
-
to_dot
-
end
-
-
1
private
-
1
def visit_Arel_Nodes_Ordering o
-
visit_edge o, "expr"
-
end
-
-
1
def visit_Arel_Nodes_TableAlias o
-
visit_edge o, "name"
-
visit_edge o, "relation"
-
end
-
-
1
def visit_Arel_Nodes_Count o
-
visit_edge o, "expressions"
-
visit_edge o, "distinct"
-
end
-
-
1
def visit_Arel_Nodes_Values o
-
visit_edge o, "expressions"
-
end
-
-
1
def visit_Arel_Nodes_StringJoin o
-
visit_edge o, "left"
-
end
-
-
1
def visit_Arel_Nodes_InnerJoin o
-
visit_edge o, "left"
-
visit_edge o, "right"
-
end
-
1
alias :visit_Arel_Nodes_OuterJoin :visit_Arel_Nodes_InnerJoin
-
-
1
def visit_Arel_Nodes_DeleteStatement o
-
visit_edge o, "relation"
-
visit_edge o, "wheres"
-
end
-
-
1
def unary o
-
visit_edge o, "expr"
-
end
-
1
alias :visit_Arel_Nodes_Group :unary
-
1
alias :visit_Arel_Nodes_BindParam :unary
-
1
alias :visit_Arel_Nodes_Grouping :unary
-
1
alias :visit_Arel_Nodes_Having :unary
-
1
alias :visit_Arel_Nodes_Limit :unary
-
1
alias :visit_Arel_Nodes_Not :unary
-
1
alias :visit_Arel_Nodes_Offset :unary
-
1
alias :visit_Arel_Nodes_On :unary
-
1
alias :visit_Arel_Nodes_Top :unary
-
1
alias :visit_Arel_Nodes_UnqualifiedColumn :unary
-
-
1
def function o
-
visit_edge o, "expressions"
-
visit_edge o, "distinct"
-
visit_edge o, "alias"
-
end
-
1
alias :visit_Arel_Nodes_Exists :function
-
1
alias :visit_Arel_Nodes_Min :function
-
1
alias :visit_Arel_Nodes_Max :function
-
1
alias :visit_Arel_Nodes_Avg :function
-
1
alias :visit_Arel_Nodes_Sum :function
-
-
1
def visit_Arel_Nodes_NamedFunction o
-
visit_edge o, "name"
-
visit_edge o, "expressions"
-
visit_edge o, "distinct"
-
visit_edge o, "alias"
-
end
-
-
1
def visit_Arel_Nodes_InsertStatement o
-
visit_edge o, "relation"
-
visit_edge o, "columns"
-
visit_edge o, "values"
-
end
-
-
1
def visit_Arel_Nodes_SelectCore o
-
visit_edge o, "source"
-
visit_edge o, "projections"
-
visit_edge o, "wheres"
-
end
-
-
1
def visit_Arel_Nodes_SelectStatement o
-
visit_edge o, "cores"
-
visit_edge o, "limit"
-
visit_edge o, "orders"
-
visit_edge o, "offset"
-
end
-
-
1
def visit_Arel_Nodes_UpdateStatement o
-
visit_edge o, "relation"
-
visit_edge o, "wheres"
-
visit_edge o, "values"
-
end
-
-
1
def visit_Arel_Table o
-
visit_edge o, "name"
-
end
-
-
1
def visit_Arel_Attribute o
-
visit_edge o, "relation"
-
visit_edge o, "name"
-
end
-
1
alias :visit_Arel_Attributes_Integer :visit_Arel_Attribute
-
1
alias :visit_Arel_Attributes_Float :visit_Arel_Attribute
-
1
alias :visit_Arel_Attributes_String :visit_Arel_Attribute
-
1
alias :visit_Arel_Attributes_Time :visit_Arel_Attribute
-
1
alias :visit_Arel_Attributes_Boolean :visit_Arel_Attribute
-
1
alias :visit_Arel_Attributes_Attribute :visit_Arel_Attribute
-
-
1
def nary o
-
o.children.each_with_index do |x,i|
-
edge(i) { visit x }
-
end
-
end
-
1
alias :visit_Arel_Nodes_And :nary
-
-
1
def binary o
-
visit_edge o, "left"
-
visit_edge o, "right"
-
end
-
1
alias :visit_Arel_Nodes_As :binary
-
1
alias :visit_Arel_Nodes_Assignment :binary
-
1
alias :visit_Arel_Nodes_Between :binary
-
1
alias :visit_Arel_Nodes_DoesNotMatch :binary
-
1
alias :visit_Arel_Nodes_Equality :binary
-
1
alias :visit_Arel_Nodes_GreaterThan :binary
-
1
alias :visit_Arel_Nodes_GreaterThanOrEqual :binary
-
1
alias :visit_Arel_Nodes_In :binary
-
1
alias :visit_Arel_Nodes_JoinSource :binary
-
1
alias :visit_Arel_Nodes_LessThan :binary
-
1
alias :visit_Arel_Nodes_LessThanOrEqual :binary
-
1
alias :visit_Arel_Nodes_Matches :binary
-
1
alias :visit_Arel_Nodes_NotEqual :binary
-
1
alias :visit_Arel_Nodes_NotIn :binary
-
1
alias :visit_Arel_Nodes_Or :binary
-
-
1
def visit_String o
-
@node_stack.last.fields << o
-
end
-
1
alias :visit_Time :visit_String
-
1
alias :visit_Date :visit_String
-
1
alias :visit_DateTime :visit_String
-
1
alias :visit_NilClass :visit_String
-
1
alias :visit_TrueClass :visit_String
-
1
alias :visit_FalseClass :visit_String
-
1
alias :visit_Arel_SqlLiteral :visit_String
-
1
alias :visit_Fixnum :visit_String
-
1
alias :visit_BigDecimal :visit_String
-
1
alias :visit_Float :visit_String
-
1
alias :visit_Symbol :visit_String
-
1
alias :visit_Arel_Nodes_SqlLiteral :visit_String
-
-
1
def visit_Hash o
-
o.each_with_index do |pair, i|
-
edge("pair_#{i}") { visit pair }
-
end
-
end
-
-
1
def visit_Array o
-
o.each_with_index do |x,i|
-
edge(i) { visit x }
-
end
-
end
-
-
1
def visit_edge o, method
-
edge(method) { visit o.send(method) }
-
end
-
-
1
def visit o
-
if node = @seen[o.object_id]
-
@edge_stack.last.to = node
-
return
-
end
-
-
node = Node.new(o.class.name, o.object_id)
-
@seen[node.id] = node
-
@nodes << node
-
with_node node do
-
super
-
end
-
end
-
-
1
def edge name
-
edge = Edge.new(name, @node_stack.last)
-
@edge_stack.push edge
-
@edges << edge
-
yield
-
@edge_stack.pop
-
end
-
-
1
def with_node node
-
if edge = @edge_stack.last
-
edge.to = node
-
end
-
-
@node_stack.push node
-
yield
-
@node_stack.pop
-
end
-
-
1
def quote string
-
string.to_s.gsub('"', '\"')
-
end
-
-
1
def to_dot
-
"digraph \"ARel\" {\nnode [width=0.375,height=0.25,shape=record];\n" +
-
@nodes.map { |node|
-
label = "<f0>#{node.name}"
-
-
node.fields.each_with_index do |field, i|
-
label << "|<f#{i + 1}>#{quote field}"
-
end
-
-
"#{node.id} [label=\"#{label}\"];"
-
}.join("\n") + "\n" + @edges.map { |edge|
-
"#{edge.from.id} -> #{edge.to.id} [label=\"#{edge.name}\"];"
-
}.join("\n") + "\n}"
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
class IBM_DB < Arel::Visitors::ToSql
-
1
private
-
-
1
def visit_Arel_Nodes_Limit o
-
"FETCH FIRST #{visit o.expr} ROWS ONLY"
-
end
-
-
end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
class Informix < Arel::Visitors::ToSql
-
1
private
-
1
def visit_Arel_Nodes_SelectStatement o
-
[
-
"SELECT",
-
(visit(o.offset) if o.offset),
-
(visit(o.limit) if o.limit),
-
o.cores.map { |x| visit_Arel_Nodes_SelectCore x }.join,
-
("ORDER BY #{o.orders.map { |x| visit x }.join(', ')}" unless o.orders.empty?),
-
(visit(o.lock) if o.lock),
-
].compact.join ' '
-
end
-
1
def visit_Arel_Nodes_SelectCore o
-
[
-
"#{o.projections.map { |x| visit x }.join ', '}",
-
("FROM #{visit o.froms}" if o.froms),
-
("WHERE #{o.wheres.map { |x| visit x }.join ' AND ' }" unless o.wheres.empty?),
-
("GROUP BY #{o.groups.map { |x| visit x }.join ', ' }" unless o.groups.empty?),
-
(visit(o.having) if o.having),
-
].compact.join ' '
-
end
-
1
def visit_Arel_Nodes_Offset o
-
"SKIP #{visit o.expr}"
-
end
-
1
def visit_Arel_Nodes_Limit o
-
"LIMIT #{visit o.expr}"
-
end
-
end
-
end
-
end
-
-
1
module Arel
-
1
module Visitors
-
###
-
# This class produces SQL for JOIN clauses but omits the "single-source"
-
# part of the Join grammar:
-
#
-
# http://www.sqlite.org/syntaxdiagrams.html#join-source
-
#
-
# This visitor is used in SelectManager#join_sql and is for backwards
-
# compatibility with Arel V1.0
-
1
module JoinSql
-
1
private
-
-
1
def visit_Arel_Nodes_SelectCore o
-
o.source.right.map { |j| visit j }.join ' '
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
class MSSQL < Arel::Visitors::ToSql
-
1
private
-
-
# `top` wouldn't really work here. I.e. User.select("distinct first_name").limit(10) would generate
-
# "select top 10 distinct first_name from users", which is invalid query! it should be
-
# "select distinct top 10 first_name from users"
-
1
def visit_Arel_Nodes_Top o
-
""
-
end
-
-
1
def visit_Arel_Nodes_SelectStatement o
-
if !o.limit && !o.offset
-
return super o
-
end
-
-
select_order_by = "ORDER BY #{o.orders.map { |x| visit x }.join(', ')}" unless o.orders.empty?
-
-
is_select_count = false
-
sql = o.cores.map { |x|
-
core_order_by = select_order_by || determine_order_by(x)
-
if select_count? x
-
x.projections = [row_num_literal(core_order_by)]
-
is_select_count = true
-
else
-
x.projections << row_num_literal(core_order_by)
-
end
-
-
visit_Arel_Nodes_SelectCore x
-
}.join
-
-
sql = "SELECT _t.* FROM (#{sql}) as _t WHERE #{get_offset_limit_clause(o)}"
-
# fixme count distinct wouldn't work with limit or offset
-
sql = "SELECT COUNT(1) as count_id FROM (#{sql}) AS subquery" if is_select_count
-
sql
-
end
-
-
1
def get_offset_limit_clause o
-
first_row = o.offset ? o.offset.expr.to_i + 1 : 1
-
last_row = o.limit ? o.limit.expr.to_i - 1 + first_row : nil
-
if last_row
-
" _row_num BETWEEN #{first_row} AND #{last_row}"
-
else
-
" _row_num >= #{first_row}"
-
end
-
end
-
-
1
def determine_order_by x
-
unless x.groups.empty?
-
"ORDER BY #{x.groups.map { |g| visit g }.join ', ' }"
-
else
-
"ORDER BY #{find_left_table_pk(x.froms)}"
-
end
-
end
-
-
1
def row_num_literal order_by
-
Nodes::SqlLiteral.new("ROW_NUMBER() OVER (#{order_by}) as _row_num")
-
end
-
-
1
def select_count? x
-
x.projections.length == 1 && Arel::Nodes::Count === x.projections.first
-
end
-
-
# fixme raise exception of there is no pk?
-
# fixme!! Table.primary_key will be depricated. What is the replacement??
-
1
def find_left_table_pk o
-
return visit o.primary_key if o.instance_of? Arel::Table
-
find_left_table_pk o.left if o.kind_of? Arel::Nodes::Join
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
class MySQL < Arel::Visitors::ToSql
-
1
private
-
1
def visit_Arel_Nodes_Union o, suppress_parens = false
-
left_result = case o.left
-
when Arel::Nodes::Union
-
visit_Arel_Nodes_Union o.left, true
-
else
-
visit o.left
-
end
-
-
right_result = case o.right
-
when Arel::Nodes::Union
-
visit_Arel_Nodes_Union o.right, true
-
else
-
visit o.right
-
end
-
-
if suppress_parens
-
"#{left_result} UNION #{right_result}"
-
else
-
"( #{left_result} UNION #{right_result} )"
-
end
-
end
-
-
1
def visit_Arel_Nodes_Bin o
-
"BINARY #{visit o.expr}"
-
end
-
-
###
-
# :'(
-
# http://dev.mysql.com/doc/refman/5.0/en/select.html#id3482214
-
1
def visit_Arel_Nodes_SelectStatement o
-
o.limit = Arel::Nodes::Limit.new(18446744073709551615) if o.offset && !o.limit
-
super
-
end
-
-
1
def visit_Arel_Nodes_SelectCore o
-
o.froms ||= Arel.sql('DUAL')
-
super
-
end
-
-
1
def visit_Arel_Nodes_UpdateStatement o
-
[
-
"UPDATE #{visit o.relation}",
-
("SET #{o.values.map { |value| visit value }.join ', '}" unless o.values.empty?),
-
("WHERE #{o.wheres.map { |x| visit x }.join ' AND '}" unless o.wheres.empty?),
-
("ORDER BY #{o.orders.map { |x| visit x }.join(', ')}" unless o.orders.empty?),
-
(visit(o.limit) if o.limit),
-
].compact.join ' '
-
end
-
-
end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
class Oracle < Arel::Visitors::ToSql
-
1
private
-
-
1
def visit_Arel_Nodes_SelectStatement o
-
o = order_hacks(o)
-
-
# if need to select first records without ORDER BY and GROUP BY and without DISTINCT
-
# then can use simple ROWNUM in WHERE clause
-
if o.limit && o.orders.empty? && !o.offset && o.cores.first.projections.first !~ /^DISTINCT /
-
o.cores.last.wheres.push Nodes::LessThanOrEqual.new(
-
Nodes::SqlLiteral.new('ROWNUM'), o.limit.expr
-
)
-
return super
-
end
-
-
if o.limit && o.offset
-
o = o.dup
-
limit = o.limit.expr.to_i
-
offset = o.offset
-
o.offset = nil
-
sql = super(o)
-
return <<-eosql
-
SELECT * FROM (
-
SELECT raw_sql_.*, rownum raw_rnum_
-
FROM (#{sql}) raw_sql_
-
WHERE rownum <= #{offset.expr.to_i + limit}
-
)
-
WHERE #{visit offset}
-
eosql
-
end
-
-
if o.limit
-
o = o.dup
-
limit = o.limit.expr
-
return "SELECT * FROM (#{super(o)}) WHERE ROWNUM <= #{visit limit}"
-
end
-
-
if o.offset
-
o = o.dup
-
offset = o.offset
-
o.offset = nil
-
sql = super(o)
-
return <<-eosql
-
SELECT * FROM (
-
SELECT raw_sql_.*, rownum raw_rnum_
-
FROM (#{sql}) raw_sql_
-
)
-
WHERE #{visit offset}
-
eosql
-
end
-
-
super
-
end
-
-
1
def visit_Arel_Nodes_Limit o
-
end
-
-
1
def visit_Arel_Nodes_Offset o
-
"raw_rnum_ > #{visit o.expr}"
-
end
-
-
1
def visit_Arel_Nodes_Except o
-
"( #{visit o.left} MINUS #{visit o.right} )"
-
end
-
-
1
def visit_Arel_Nodes_UpdateStatement o
-
# Oracle does not allow ORDER BY/LIMIT in UPDATEs.
-
if o.orders.any? && o.limit.nil?
-
# However, there is no harm in silently eating the ORDER BY clause if no LIMIT has been provided,
-
# otherwise let the user deal with the error
-
o = o.dup
-
o.orders = []
-
end
-
-
super
-
end
-
-
###
-
# Hacks for the order clauses specific to Oracle
-
1
def order_hacks o
-
return o if o.orders.empty?
-
return o unless o.cores.any? do |core|
-
core.projections.any? do |projection|
-
/DISTINCT.*FIRST_VALUE/ === projection
-
end
-
end
-
# Previous version with join and split broke ORDER BY clause
-
# if it contained functions with several arguments (separated by ',').
-
#
-
# orders = o.orders.map { |x| visit x }.join(', ').split(',')
-
orders = o.orders.map do |x|
-
string = visit x
-
if string.include?(',')
-
split_order_string(string)
-
else
-
string
-
end
-
end.flatten
-
o.orders = []
-
orders.each_with_index do |order, i|
-
o.orders <<
-
Nodes::SqlLiteral.new("alias_#{i}__#{' DESC' if /\bdesc$/i === order}")
-
end
-
o
-
end
-
-
# Split string by commas but count opening and closing brackets
-
# and ignore commas inside brackets.
-
1
def split_order_string(string)
-
array = []
-
i = 0
-
string.split(',').each do |part|
-
if array[i]
-
array[i] << ',' << part
-
else
-
# to ensure that array[i] will be String and not Arel::Nodes::SqlLiteral
-
array[i] = '' << part
-
end
-
i += 1 if array[i].count('(') == array[i].count(')')
-
end
-
array
-
end
-
-
end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
class OrderClauses < Arel::Visitors::ToSql
-
1
private
-
-
1
def visit_Arel_Nodes_SelectStatement o
-
o.orders.map { |x| visit x }
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
class PostgreSQL < Arel::Visitors::ToSql
-
1
private
-
-
1
def visit_Arel_Nodes_Matches o
-
"#{visit o.left} ILIKE #{visit o.right}"
-
end
-
-
1
def visit_Arel_Nodes_DoesNotMatch o
-
"#{visit o.left} NOT ILIKE #{visit o.right}"
-
end
-
-
1
def visit_Arel_Nodes_DistinctOn o
-
"DISTINCT ON ( #{visit o.expr} )"
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
class SQLite < Arel::Visitors::ToSql
-
1
private
-
-
# Locks are not supported in SQLite
-
1
def visit_Arel_Nodes_Lock o
-
end
-
-
1
def visit_Arel_Nodes_SelectStatement o
-
o.limit = Arel::Nodes::Limit.new(-1) if o.offset && !o.limit
-
super
-
end
-
end
-
end
-
end
-
1
require 'bigdecimal'
-
1
require 'date'
-
-
1
module Arel
-
1
module Visitors
-
1
class ToSql < Arel::Visitors::Visitor
-
1
def initialize pool
-
1
@pool = pool
-
1
@connection = nil
-
1
@quoted_tables = {}
-
1
@quoted_columns = {}
-
end
-
-
1
def accept object
-
self.last_column = nil
-
@pool.with_connection do |conn|
-
@connection = conn
-
super
-
end
-
end
-
-
1
private
-
1
def last_column= col
-
Thread.current[:arel_visitors_to_sql_last_column] = col
-
end
-
-
1
def last_column
-
Thread.current[:arel_visitors_to_sql_last_column]
-
end
-
-
1
def visit_Arel_Nodes_DeleteStatement o
-
[
-
"DELETE FROM #{visit o.relation}",
-
("WHERE #{o.wheres.map { |x| visit x }.join ' AND '}" unless o.wheres.empty?)
-
].compact.join ' '
-
end
-
-
# FIXME: we should probably have a 2-pass visitor for this
-
1
def build_subselect key, o
-
stmt = Nodes::SelectStatement.new
-
core = stmt.cores.first
-
core.froms = o.relation
-
core.wheres = o.wheres
-
core.projections = [key]
-
stmt.limit = o.limit
-
stmt.orders = o.orders
-
stmt
-
end
-
-
1
def visit_Arel_Nodes_UpdateStatement o
-
if o.orders.empty? && o.limit.nil?
-
wheres = o.wheres
-
else
-
key = o.key
-
unless key
-
warn(<<-eowarn) if $VERBOSE
-
(#{caller.first}) Using UpdateManager without setting UpdateManager#key is
-
deprecated and support will be removed in ARel 3.0.0. Please set the primary
-
key on UpdateManager using UpdateManager#key=
-
eowarn
-
key = o.relation.primary_key
-
end
-
-
wheres = [Nodes::In.new(key, [build_subselect(key, o)])]
-
end
-
-
[
-
"UPDATE #{visit o.relation}",
-
("SET #{o.values.map { |value| visit value }.join ', '}" unless o.values.empty?),
-
("WHERE #{wheres.map { |x| visit x }.join ' AND '}" unless wheres.empty?),
-
].compact.join ' '
-
end
-
-
1
def visit_Arel_Nodes_InsertStatement o
-
[
-
"INSERT INTO #{visit o.relation}",
-
-
("(#{o.columns.map { |x|
-
quote_column_name x.name
-
}.join ', '})" unless o.columns.empty?),
-
-
(visit o.values if o.values),
-
].compact.join ' '
-
end
-
-
1
def visit_Arel_Nodes_Exists o
-
"EXISTS (#{visit o.expressions})#{
-
o.alias ? " AS #{visit o.alias}" : ''}"
-
end
-
-
1
def visit_Arel_Nodes_True o
-
"TRUE"
-
end
-
-
1
def visit_Arel_Nodes_False o
-
"FALSE"
-
end
-
-
1
def table_exists? name
-
@pool.table_exists? name
-
end
-
-
1
def column_for attr
-
name = attr.name.to_s
-
table = attr.relation.table_name
-
-
return nil unless table_exists? table
-
-
column_cache[table][name]
-
end
-
-
1
def column_cache
-
@pool.columns_hash
-
end
-
-
1
def visit_Arel_Nodes_Values o
-
"VALUES (#{o.expressions.zip(o.columns).map { |value, attr|
-
if Nodes::SqlLiteral === value
-
visit value
-
else
-
quote(value, attr && column_for(attr))
-
end
-
}.join ', '})"
-
end
-
-
1
def visit_Arel_Nodes_SelectStatement o
-
[
-
(visit(o.with) if o.with),
-
o.cores.map { |x| visit_Arel_Nodes_SelectCore x }.join,
-
("ORDER BY #{o.orders.map { |x| visit x }.join(', ')}" unless o.orders.empty?),
-
(visit(o.limit) if o.limit),
-
(visit(o.offset) if o.offset),
-
(visit(o.lock) if o.lock),
-
].compact.join ' '
-
end
-
-
1
def visit_Arel_Nodes_SelectCore o
-
[
-
"SELECT",
-
(visit(o.top) if o.top),
-
(visit(o.set_quantifier) if o.set_quantifier),
-
("#{o.projections.map { |x| visit x }.join ', '}" unless o.projections.empty?),
-
("FROM #{visit(o.source)}" if o.source && !o.source.empty?),
-
("WHERE #{o.wheres.map { |x| visit x }.join ' AND ' }" unless o.wheres.empty?),
-
("GROUP BY #{o.groups.map { |x| visit x }.join ', ' }" unless o.groups.empty?),
-
(visit(o.having) if o.having),
-
].compact.join ' '
-
end
-
-
1
def visit_Arel_Nodes_Bin o
-
visit o.expr
-
end
-
-
1
def visit_Arel_Nodes_Distinct o
-
'DISTINCT'
-
end
-
-
1
def visit_Arel_Nodes_DistinctOn o
-
raise NotImplementedError, 'DISTINCT ON not implemented for this db'
-
end
-
-
1
def visit_Arel_Nodes_With o
-
"WITH #{o.children.map { |x| visit x }.join(', ')}"
-
end
-
-
1
def visit_Arel_Nodes_WithRecursive o
-
"WITH RECURSIVE #{o.children.map { |x| visit x }.join(', ')}"
-
end
-
-
1
def visit_Arel_Nodes_Union o
-
"( #{visit o.left} UNION #{visit o.right} )"
-
end
-
-
1
def visit_Arel_Nodes_UnionAll o
-
"( #{visit o.left} UNION ALL #{visit o.right} )"
-
end
-
-
1
def visit_Arel_Nodes_Intersect o
-
"( #{visit o.left} INTERSECT #{visit o.right} )"
-
end
-
-
1
def visit_Arel_Nodes_Except o
-
"( #{visit o.left} EXCEPT #{visit o.right} )"
-
end
-
-
1
def visit_Arel_Nodes_Having o
-
"HAVING #{visit o.expr}"
-
end
-
-
1
def visit_Arel_Nodes_Offset o
-
"OFFSET #{visit o.expr}"
-
end
-
-
1
def visit_Arel_Nodes_Limit o
-
"LIMIT #{visit o.expr}"
-
end
-
-
# FIXME: this does nothing on most databases, but does on MSSQL
-
1
def visit_Arel_Nodes_Top o
-
""
-
end
-
-
1
def visit_Arel_Nodes_Lock o
-
visit o.expr
-
end
-
-
1
def visit_Arel_Nodes_Grouping o
-
"(#{visit o.expr})"
-
end
-
-
1
def visit_Arel_Nodes_Ascending o
-
"#{visit o.expr} ASC"
-
end
-
-
1
def visit_Arel_Nodes_Descending o
-
"#{visit o.expr} DESC"
-
end
-
-
1
def visit_Arel_Nodes_Group o
-
visit o.expr
-
end
-
-
1
def visit_Arel_Nodes_NamedFunction o
-
"#{o.name}(#{o.distinct ? 'DISTINCT ' : ''}#{o.expressions.map { |x|
-
visit x
-
}.join(', ')})#{o.alias ? " AS #{visit o.alias}" : ''}"
-
end
-
-
1
def visit_Arel_Nodes_Count o
-
"COUNT(#{o.distinct ? 'DISTINCT ' : ''}#{o.expressions.map { |x|
-
visit x
-
}.join(', ')})#{o.alias ? " AS #{visit o.alias}" : ''}"
-
end
-
-
1
def visit_Arel_Nodes_Sum o
-
"SUM(#{o.expressions.map { |x|
-
visit x }.join(', ')})#{o.alias ? " AS #{visit o.alias}" : ''}"
-
end
-
-
1
def visit_Arel_Nodes_Max o
-
"MAX(#{o.expressions.map { |x|
-
visit x }.join(', ')})#{o.alias ? " AS #{visit o.alias}" : ''}"
-
end
-
-
1
def visit_Arel_Nodes_Min o
-
"MIN(#{o.expressions.map { |x|
-
visit x }.join(', ')})#{o.alias ? " AS #{visit o.alias}" : ''}"
-
end
-
-
1
def visit_Arel_Nodes_Avg o
-
"AVG(#{o.expressions.map { |x|
-
visit x }.join(', ')})#{o.alias ? " AS #{visit o.alias}" : ''}"
-
end
-
-
1
def visit_Arel_Nodes_TableAlias o
-
"#{visit o.relation} #{quote_table_name o.name}"
-
end
-
-
1
def visit_Arel_Nodes_Between o
-
"#{visit o.left} BETWEEN #{visit o.right}"
-
end
-
-
1
def visit_Arel_Nodes_GreaterThanOrEqual o
-
"#{visit o.left} >= #{visit o.right}"
-
end
-
-
1
def visit_Arel_Nodes_GreaterThan o
-
"#{visit o.left} > #{visit o.right}"
-
end
-
-
1
def visit_Arel_Nodes_LessThanOrEqual o
-
"#{visit o.left} <= #{visit o.right}"
-
end
-
-
1
def visit_Arel_Nodes_LessThan o
-
"#{visit o.left} < #{visit o.right}"
-
end
-
-
1
def visit_Arel_Nodes_Matches o
-
"#{visit o.left} LIKE #{visit o.right}"
-
end
-
-
1
def visit_Arel_Nodes_DoesNotMatch o
-
"#{visit o.left} NOT LIKE #{visit o.right}"
-
end
-
-
1
def visit_Arel_Nodes_JoinSource o
-
[
-
(visit(o.left) if o.left),
-
o.right.map { |j| visit j }.join(' ')
-
].compact.join ' '
-
end
-
-
1
def visit_Arel_Nodes_StringJoin o
-
visit o.left
-
end
-
-
1
def visit_Arel_Nodes_OuterJoin o
-
"LEFT OUTER JOIN #{visit o.left} #{visit o.right}"
-
end
-
-
1
def visit_Arel_Nodes_InnerJoin o
-
"INNER JOIN #{visit o.left} #{visit o.right if o.right}"
-
end
-
-
1
def visit_Arel_Nodes_On o
-
"ON #{visit o.expr}"
-
end
-
-
1
def visit_Arel_Nodes_Not o
-
"NOT (#{visit o.expr})"
-
end
-
-
1
def visit_Arel_Table o
-
if o.table_alias
-
"#{quote_table_name o.name} #{quote_table_name o.table_alias}"
-
else
-
quote_table_name o.name
-
end
-
end
-
-
1
def visit_Arel_Nodes_In o
-
"#{visit o.left} IN (#{visit o.right})"
-
end
-
-
1
def visit_Arel_Nodes_NotIn o
-
"#{visit o.left} NOT IN (#{visit o.right})"
-
end
-
-
1
def visit_Arel_Nodes_And o
-
o.children.map { |x| visit x }.join ' AND '
-
end
-
-
1
def visit_Arel_Nodes_Or o
-
"#{visit o.left} OR #{visit o.right}"
-
end
-
-
1
def visit_Arel_Nodes_Assignment o
-
right = quote(o.right, column_for(o.left))
-
"#{visit o.left} = #{right}"
-
end
-
-
1
def visit_Arel_Nodes_Equality o
-
right = o.right
-
-
if right.nil?
-
"#{visit o.left} IS NULL"
-
else
-
"#{visit o.left} = #{visit right}"
-
end
-
end
-
-
1
def visit_Arel_Nodes_NotEqual o
-
right = o.right
-
-
if right.nil?
-
"#{visit o.left} IS NOT NULL"
-
else
-
"#{visit o.left} != #{visit right}"
-
end
-
end
-
-
1
def visit_Arel_Nodes_As o
-
"#{visit o.left} AS #{visit o.right}"
-
end
-
-
1
def visit_Arel_Nodes_UnqualifiedColumn o
-
"#{quote_column_name o.name}"
-
end
-
-
1
def visit_Arel_Attributes_Attribute o
-
self.last_column = column_for o
-
join_name = o.relation.table_alias || o.relation.name
-
"#{quote_table_name join_name}.#{quote_column_name o.name}"
-
end
-
1
alias :visit_Arel_Attributes_Integer :visit_Arel_Attributes_Attribute
-
1
alias :visit_Arel_Attributes_Float :visit_Arel_Attributes_Attribute
-
1
alias :visit_Arel_Attributes_Decimal :visit_Arel_Attributes_Attribute
-
1
alias :visit_Arel_Attributes_String :visit_Arel_Attributes_Attribute
-
1
alias :visit_Arel_Attributes_Time :visit_Arel_Attributes_Attribute
-
1
alias :visit_Arel_Attributes_Boolean :visit_Arel_Attributes_Attribute
-
-
1
def literal o; o end
-
-
1
alias :visit_Arel_Nodes_BindParam :literal
-
1
alias :visit_Arel_Nodes_SqlLiteral :literal
-
1
alias :visit_Arel_SqlLiteral :literal # This is deprecated
-
1
alias :visit_Bignum :literal
-
1
alias :visit_Fixnum :literal
-
-
1
def quoted o; quote(o, last_column) end
-
-
1
alias :visit_ActiveSupport_Multibyte_Chars :quoted
-
1
alias :visit_ActiveSupport_StringInquirer :quoted
-
1
alias :visit_BigDecimal :quoted
-
1
alias :visit_Class :quoted
-
1
alias :visit_Date :quoted
-
1
alias :visit_DateTime :quoted
-
1
alias :visit_FalseClass :quoted
-
1
alias :visit_Float :quoted
-
1
alias :visit_Hash :quoted
-
1
alias :visit_NilClass :quoted
-
1
alias :visit_String :quoted
-
1
alias :visit_Symbol :quoted
-
1
alias :visit_Time :quoted
-
1
alias :visit_TrueClass :quoted
-
-
1
def visit_Arel_Nodes_InfixOperation o
-
"#{visit o.left} #{o.operator} #{visit o.right}"
-
end
-
-
1
alias :visit_Arel_Nodes_Addition :visit_Arel_Nodes_InfixOperation
-
1
alias :visit_Arel_Nodes_Subtraction :visit_Arel_Nodes_InfixOperation
-
1
alias :visit_Arel_Nodes_Multiplication :visit_Arel_Nodes_InfixOperation
-
1
alias :visit_Arel_Nodes_Division :visit_Arel_Nodes_InfixOperation
-
-
1
def visit_Array o
-
o.empty? ? 'NULL' : o.map { |x| visit x }.join(', ')
-
end
-
-
1
def quote value, column = nil
-
@connection.quote value, column
-
end
-
-
1
def quote_table_name name
-
return name if Arel::Nodes::SqlLiteral === name
-
@quoted_tables[name] ||= @connection.quote_table_name(name)
-
end
-
-
1
def quote_column_name name
-
@quoted_columns[name] ||= Arel::Nodes::SqlLiteral === name ? name : @connection.quote_column_name(name)
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
class Visitor
-
1
def accept object
-
visit object
-
end
-
-
1
private
-
-
1
DISPATCH = Hash.new do |hash, klass|
-
hash[klass] = "visit_#{(klass.name || '').gsub('::', '_')}"
-
end
-
-
1
def dispatch
-
DISPATCH
-
end
-
-
1
def visit object
-
send dispatch[object.class], object
-
rescue NoMethodError => e
-
raise e if respond_to?(dispatch[object.class], true)
-
superklass = object.class.ancestors.find { |klass|
-
respond_to?(dispatch[klass], true)
-
}
-
raise(TypeError, "Cannot visit #{object.class}") unless superklass
-
dispatch[object.class] = dispatch[superklass]
-
retry
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
class WhereSql < Arel::Visitors::ToSql
-
1
def visit_Arel_Nodes_SelectCore o
-
"WHERE #{o.wheres.map { |x| visit x }.join ' AND ' }"
-
end
-
end
-
end
-
end
-
# A wrapper for OpenBSD's bcrypt/crypt_blowfish password-hashing algorithm.
-
-
1
if RUBY_PLATFORM == "java"
-
require 'java'
-
else
-
1
require "openssl"
-
end
-
-
1
if defined?(RUBY_ENGINE) and RUBY_ENGINE == "maglev"
-
require 'bcrypt_engine'
-
else
-
1
require 'bcrypt_ext'
-
end
-
-
# A Ruby library implementing OpenBSD's bcrypt()/crypt_blowfish algorithm for
-
# hashing passwords.
-
1
module BCrypt
-
1
module Errors
-
1
class InvalidSalt < StandardError; end # The salt parameter provided to bcrypt() is invalid.
-
1
class InvalidHash < StandardError; end # The hash parameter provided to bcrypt() is invalid.
-
1
class InvalidCost < StandardError; end # The cost parameter provided to bcrypt() is invalid.
-
1
class InvalidSecret < StandardError; end # The secret parameter provided to bcrypt() is invalid.
-
end
-
-
# A Ruby wrapper for the bcrypt() C extension calls and the Java calls.
-
1
class Engine
-
# The default computational expense parameter.
-
1
DEFAULT_COST = 10
-
# The minimum cost supported by the algorithm.
-
1
MIN_COST = 4
-
# Maximum possible size of bcrypt() salts.
-
1
MAX_SALT_LENGTH = 16
-
-
1
if RUBY_PLATFORM != "java"
-
# C-level routines which, if they don't get the right input, will crash the
-
# hell out of the Ruby process.
-
1
private_class_method :__bc_salt
-
1
private_class_method :__bc_crypt
-
end
-
-
# Given a secret and a valid salt (see BCrypt::Engine.generate_salt) calculates
-
# a bcrypt() password hash.
-
1
def self.hash_secret(secret, salt, cost = nil)
-
if valid_secret?(secret)
-
if valid_salt?(salt)
-
if cost.nil?
-
cost = autodetect_cost(salt)
-
end
-
-
if RUBY_PLATFORM == "java"
-
Java.bcrypt_jruby.BCrypt.hashpw(secret.to_s, salt.to_s)
-
else
-
__bc_crypt(secret.to_s, salt)
-
end
-
else
-
raise Errors::InvalidSalt.new("invalid salt")
-
end
-
else
-
raise Errors::InvalidSecret.new("invalid secret")
-
end
-
end
-
-
# Generates a random salt with a given computational cost.
-
1
def self.generate_salt(cost = DEFAULT_COST)
-
cost = cost.to_i
-
if cost > 0
-
if cost < MIN_COST
-
cost = MIN_COST
-
end
-
if RUBY_PLATFORM == "java"
-
Java.bcrypt_jruby.BCrypt.gensalt(cost)
-
else
-
prefix = "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"
-
__bc_salt(prefix, cost, OpenSSL::Random.random_bytes(MAX_SALT_LENGTH))
-
end
-
else
-
raise Errors::InvalidCost.new("cost must be numeric and > 0")
-
end
-
end
-
-
# Returns true if +salt+ is a valid bcrypt() salt, false if not.
-
1
def self.valid_salt?(salt)
-
!!(salt =~ /^\$[0-9a-z]{2,}\$[0-9]{2,}\$[A-Za-z0-9\.\/]{22,}$/)
-
end
-
-
# Returns true if +secret+ is a valid bcrypt() secret, false if not.
-
1
def self.valid_secret?(secret)
-
secret.respond_to?(:to_s)
-
end
-
-
# Returns the cost factor which will result in computation times less than +upper_time_limit_in_ms+.
-
#
-
# Example:
-
#
-
# BCrypt.calibrate(200) #=> 10
-
# BCrypt.calibrate(1000) #=> 12
-
#
-
# # should take less than 200ms
-
# BCrypt::Password.create("woo", :cost => 10)
-
#
-
# # should take less than 1000ms
-
# BCrypt::Password.create("woo", :cost => 12)
-
1
def self.calibrate(upper_time_limit_in_ms)
-
40.times do |i|
-
start_time = Time.now
-
Password.create("testing testing", :cost => i+1)
-
end_time = Time.now - start_time
-
return i if end_time * 1_000 > upper_time_limit_in_ms
-
end
-
end
-
-
# Autodetects the cost from the salt string.
-
1
def self.autodetect_cost(salt)
-
salt[4..5].to_i
-
end
-
end
-
-
# A password management class which allows you to safely store users' passwords and compare them.
-
#
-
# Example usage:
-
#
-
# include BCrypt
-
#
-
# # hash a user's password
-
# @password = Password.create("my grand secret")
-
# @password #=> "$2a$10$GtKs1Kbsig8ULHZzO1h2TetZfhO4Fmlxphp8bVKnUlZCBYYClPohG"
-
#
-
# # store it safely
-
# @user.update_attribute(:password, @password)
-
#
-
# # read it back
-
# @user.reload!
-
# @db_password = Password.new(@user.password)
-
#
-
# # compare it after retrieval
-
# @db_password == "my grand secret" #=> true
-
# @db_password == "a paltry guess" #=> false
-
#
-
1
class Password < String
-
# The hash portion of the stored password hash.
-
1
attr_reader :checksum
-
# The salt of the store password hash (including version and cost).
-
1
attr_reader :salt
-
# The version of the bcrypt() algorithm used to create the hash.
-
1
attr_reader :version
-
# The cost factor used to create the hash.
-
1
attr_reader :cost
-
-
1
class << self
-
# Hashes a secret, returning a BCrypt::Password instance. Takes an optional <tt>:cost</tt> option, which is a
-
# logarithmic variable which determines how computational expensive the hash is to calculate (a <tt>:cost</tt> of
-
# 4 is twice as much work as a <tt>:cost</tt> of 3). The higher the <tt>:cost</tt> the harder it becomes for
-
# attackers to try to guess passwords (even if a copy of your database is stolen), but the slower it is to check
-
# users' passwords.
-
#
-
# Example:
-
#
-
# @password = BCrypt::Password.create("my secret", :cost => 13)
-
1
def create(secret, options = { :cost => BCrypt::Engine::DEFAULT_COST })
-
raise ArgumentError if options[:cost] > 31
-
Password.new(BCrypt::Engine.hash_secret(secret, BCrypt::Engine.generate_salt(options[:cost]), options[:cost]))
-
end
-
end
-
-
# Initializes a BCrypt::Password instance with the data from a stored hash.
-
1
def initialize(raw_hash)
-
if valid_hash?(raw_hash)
-
self.replace(raw_hash)
-
@version, @cost, @salt, @checksum = split_hash(self)
-
else
-
raise Errors::InvalidHash.new("invalid hash")
-
end
-
end
-
-
# Compares a potential secret against the hash. Returns true if the secret is the original secret, false otherwise.
-
1
def ==(secret)
-
super(BCrypt::Engine.hash_secret(secret, @salt))
-
end
-
1
alias_method :is_password?, :==
-
-
1
private
-
# Returns true if +h+ is a valid hash.
-
1
def valid_hash?(h)
-
h =~ /^\$[0-9a-z]{2}\$[0-9]{2}\$[A-Za-z0-9\.\/]{53}$/
-
end
-
-
# call-seq:
-
# split_hash(raw_hash) -> version, cost, salt, hash
-
#
-
# Splits +h+ into version, cost, salt, and hash and returns them in that order.
-
1
def split_hash(h)
-
_, v, c, mash = h.split('$')
-
return v, c.to_i, h[0, 29].to_str, mash[-31, 31].to_str
-
end
-
end
-
end
-
# config/initializers/will_paginate.rb
-
#
-
# This extension code was written by Isaac Bowen, originally found
-
# at http://isaacbowen.com/blog/using-will_paginate-action_view-and-bootstrap/
-
-
1
require 'will_paginate/view_helpers/action_view'
-
-
1
module WillPaginate
-
1
module ActionView
-
1
def will_paginate(collection = nil, options = {})
-
options, collection = collection, nil if collection.is_a? Hash
-
# Taken from original will_paginate code to handle if the helper is not passed a collection object.
-
collection ||= infer_collection_from_controller
-
options[:renderer] ||= BootstrapLinkRenderer
-
super.try :html_safe
-
end
-
-
1
class BootstrapLinkRenderer < LinkRenderer
-
1
protected
-
-
1
def html_container(html)
-
tag :div, tag(:ul, html), container_attributes
-
end
-
-
1
def page_number(page)
-
tag :li, link(page, page, :rel => rel_value(page)), :class => ('active' if page == current_page)
-
end
-
-
1
def gap
-
tag :li, link(super, '#'), :class => 'disabled'
-
end
-
-
1
def previous_or_next_page(page, text, classname)
-
tag :li, link(text, page || '#'), :class => [classname[0..3], classname, ('disabled' unless page)].join(' ')
-
end
-
end
-
end
-
end
-
1
require "bootstrap-will_paginate/version"
-
-
1
module Bootstrap
-
1
module Willpaginate
-
1
class Engine < ::Rails::Engine
-
end
-
end
-
end
-
1
module Bootstrap
-
1
module Willpaginate
-
1
VERSION = "0.0.5"
-
end
-
end
-
1
require 'timeout'
-
1
require 'nokogiri'
-
1
require 'xpath'
-
1
module Capybara
-
1
class CapybaraError < StandardError; end
-
1
class DriverNotFoundError < CapybaraError; end
-
1
class FrozenInTime < CapybaraError; end
-
1
class ElementNotFound < CapybaraError; end
-
1
class ExpectationNotMet < ElementNotFound; end
-
1
class FileNotFound < CapybaraError; end
-
1
class UnselectNotAllowed < CapybaraError; end
-
1
class NotSupportedByDriverError < CapybaraError; end
-
1
class TimeoutError < CapybaraError; end
-
1
class LocateHiddenElementError < CapybaraError; end
-
1
class InfiniteRedirectError < TimeoutError; end
-
-
1
class << self
-
1
attr_accessor :asset_root, :app_host, :run_server, :default_host
-
1
attr_accessor :server_port, :server_boot_timeout
-
1
attr_accessor :default_selector, :default_wait_time, :ignore_hidden_elements, :prefer_visible_elements
-
1
attr_accessor :save_and_open_page_path, :automatic_reload
-
-
##
-
#
-
# Configure Capybara to suit your needs.
-
#
-
# Capybara.configure do |config|
-
# config.run_server = false
-
# config.app_host = 'http://www.google.com'
-
# end
-
#
-
# === Configurable options
-
#
-
# [asset_root = String] Where static assets are located, used by save_and_open_page
-
# [app_host = String] The default host to use when giving a relative URL to visit
-
# [run_server = Boolean] Whether to start a Rack server for the given Rack app (Default: true)
-
# [default_selector = :css/:xpath] Methods which take a selector use the given type by default (Default: CSS)
-
# [default_wait_time = Integer] The number of seconds to wait for asynchronous processes to finish (Default: 2)
-
# [ignore_hidden_elements = Boolean] Whether to ignore hidden elements on the page (Default: false)
-
# [prefer_visible_elements = Boolean] Whether to prefer visible elements over hidden elements (Default: true)
-
# [automatic_reload = Boolean] Whether to automatically reload elements as Capybara is waiting (Default: true)
-
# [save_and_open_page_path = String] Where to put pages saved through save_and_open_page (Default: Dir.pwd)
-
#
-
# === DSL Options
-
#
-
# when using capybara/dsl, the following options are also available:
-
#
-
# [default_driver = Symbol] The name of the driver to use by default. (Default: :rack_test)
-
# [javascript_driver = Symbol] The name of a driver to use for JavaScript enabled tests. (Default: :selenium)
-
#
-
1
def configure
-
1
yield self
-
end
-
-
##
-
#
-
# Register a new driver for Capybara.
-
#
-
# Capybara.register_driver :rack_test do |app|
-
# Capybara::Driver::RackTest.new(app)
-
# end
-
#
-
# @param [Symbol] name The name of the new driver
-
# @yield [app] This block takes a rack app and returns a Capybara driver
-
# @yieldparam [<Rack>] app The rack application that this driver runs agains. May be nil.
-
# @yieldreturn [Capybara::Driver::Base] A Capybara driver instance
-
#
-
1
def register_driver(name, &block)
-
2
drivers[name] = block
-
end
-
-
##
-
#
-
# Add a new selector to Capybara. Selectors can be used by various methods in Capybara
-
# to find certain elements on the page in a more convenient way. For example adding a
-
# selector to find certain table rows might look like this:
-
#
-
# Capybara.add_selector(:row) do
-
# xpath { |num| ".//tbody/tr[#{num}]" }
-
# end
-
#
-
# This makes it possible to use this selector in a variety of ways:
-
#
-
# find(:row, 3)
-
# page.find('table#myTable').find(:row, 3).text
-
# page.find('table#myTable').has_selector?(:row, 3)
-
# within(:row, 3) { page.should have_content('$100.000') }
-
#
-
# It might be convenient to specify that the selector is automatically chosen for certain
-
# values. This way you don't have to explicitly specify that you are looking for a row, or
-
# an id. Let's say we want Capybara to treat any Symbols sent into methods like find to be
-
# treated as though they were element ids. We could achieve this like so:
-
#
-
# Capybara.add_selector(:id) do
-
# xpath { |id| XPath.descendant[XPath.attr(:id) == id.to_s] }
-
# match { |value| value.is_a?(Symbol) }
-
# end
-
#
-
# Now we can retrieve elements by id like this:
-
#
-
# find(:post_123)
-
#
-
# Note that this particular selector already ships with Capybara.
-
#
-
# @param [Symbol] name The name of the selector to add
-
# @yield A block executed in the context of the new {Capybara::Selector}
-
#
-
1
def add_selector(name, &block)
-
Capybara::Selector.add(name, &block)
-
end
-
-
1
def drivers
-
2
@drivers ||= {}
-
end
-
-
##
-
#
-
# Register a proc that Capybara will call to run the Rack application.
-
#
-
# Capybara.server do |app, port|
-
# require 'rack/handler/mongrel'
-
# Rack::Handler::Mongrel.run(app, :Port => port)
-
# end
-
#
-
# By default, Capybara will try to run thin, falling back to webrick.
-
#
-
# @yield [app, port] This block recieves a rack app and port and should run a Rack handler
-
#
-
1
def server(&block)
-
1
if block_given?
-
1
@server = block
-
else
-
@server
-
end
-
end
-
-
##
-
#
-
# Wraps the given string, which should contain an HTML document or fragment
-
# in a {Capybara::Node::Simple} which exposes all {Capybara::Node::Matchers} and
-
# {Capybara::Node::Finders}. This allows you to query any string containing
-
# HTML in the exact same way you would query the current document in a Capybara
-
# session. For example:
-
#
-
# node = Capybara.string <<-HTML
-
# <ul>
-
# <li id="home">Home</li>
-
# <li id="projects">Projects</li>
-
# </ul>
-
# HTML
-
#
-
# node.find('#projects').text # => 'Projects'
-
# node.has_selector?('li#home', :text => 'Home')
-
# node.has_selector?(:projects)
-
# node.find('ul').find('li').text # => 'Home'
-
#
-
# @param [String] html An html fragment or document
-
# @return [Capybara::Node::Simple] A node which has Capybara's finders and matchers
-
#
-
1
def string(html)
-
Capybara::Node::Simple.new(html)
-
end
-
-
##
-
#
-
# Runs Capybara's default server for the given application and port
-
# under most circumstances you should not have to call this method
-
# manually.
-
#
-
# @param [Rack Application] app The rack application to run
-
# @param [Fixnum] port The port to run the application on
-
#
-
1
def run_default_server(app, port)
-
begin
-
require 'rack/handler/thin'
-
Thin::Logging.silent = true
-
Rack::Handler::Thin.run(app, :Port => port)
-
rescue LoadError
-
require 'rack/handler/webrick'
-
Rack::Handler::WEBrick.run(app, :Port => port, :AccessLog => [], :Logger => WEBrick::Log::new(nil, 0))
-
end
-
end
-
-
1
def deprecate(method, alternate_method)
-
warn "DEPRECATED: ##{method} is deprecated, please use ##{alternate_method} instead"
-
end
-
end
-
-
1
autoload :Server, 'capybara/server'
-
1
autoload :Session, 'capybara/session'
-
1
autoload :Selector, 'capybara/selector'
-
1
autoload :VERSION, 'capybara/version'
-
-
1
module Node
-
1
autoload :Base, 'capybara/node/base'
-
1
autoload :Simple, 'capybara/node/simple'
-
1
autoload :Element, 'capybara/node/element'
-
1
autoload :Document, 'capybara/node/document'
-
1
autoload :Finders, 'capybara/node/finders'
-
1
autoload :Matchers, 'capybara/node/matchers'
-
1
autoload :Actions, 'capybara/node/actions'
-
end
-
-
1
module Driver
-
1
autoload :Base, 'capybara/driver/base'
-
1
autoload :Node, 'capybara/driver/node'
-
-
1
class Selenium
-
1
def initialize(*args)
-
raise "Capybara::Driver::Selenium has been renamed to Capybara::Selenium::Driver"
-
end
-
end
-
-
1
class RackTest
-
1
def initialize(*args)
-
raise "Capybara::Driver::RackTest has been renamed to Capybara::RackTest::Driver"
-
end
-
end
-
end
-
-
1
module RackTest
-
1
autoload :Driver, 'capybara/rack_test/driver'
-
1
autoload :Node, 'capybara/rack_test/node'
-
1
autoload :Form, 'capybara/rack_test/form'
-
1
autoload :Browser, 'capybara/rack_test/browser'
-
end
-
-
1
module Selenium
-
1
autoload :Node, 'capybara/selenium/node'
-
1
autoload :Driver, 'capybara/selenium/driver'
-
end
-
end
-
-
1
Capybara.configure do |config|
-
1
config.run_server = true
-
1
config.server {|app, port| Capybara.run_default_server(app, port)}
-
1
config.server_boot_timeout = 10
-
1
config.default_selector = :css
-
1
config.default_wait_time = 2
-
1
config.ignore_hidden_elements = false
-
1
config.prefer_visible_elements = true
-
1
config.default_host = "http://www.example.com"
-
1
config.automatic_reload = true
-
end
-
-
1
Capybara.register_driver :rack_test do |app|
-
Capybara::RackTest::Driver.new(app)
-
end
-
-
1
Capybara.register_driver :selenium do |app|
-
Capybara::Selenium::Driver.new(app)
-
end
-
1
require 'capybara'
-
-
1
require 'capybara/dsl'
-
1
require 'capybara/rspec/matchers'
-
-
1
World(Capybara::DSL)
-
1
World(Capybara::RSpecMatchers)
-
-
1
After do
-
Capybara.reset_sessions!
-
end
-
-
1
Before '@javascript' do
-
Capybara.current_driver = Capybara.javascript_driver
-
end
-
-
1
Before do |scenario|
-
scenario.source_tag_names.each do |tag|
-
driver_name = tag.sub(/^@/, '').to_sym
-
if Capybara.drivers.has_key?(driver_name)
-
Capybara.current_driver = driver_name
-
end
-
end
-
end
-
-
1
After do
-
Capybara.use_default_driver
-
end
-
1
module Capybara
-
1
module Driver
-
1
class Node
-
1
attr_reader :driver, :native
-
-
1
def initialize(driver, native)
-
@driver = driver
-
@native = native
-
end
-
-
1
def text
-
raise NotImplementedError
-
end
-
-
1
def [](name)
-
raise NotImplementedError
-
end
-
-
1
def value
-
raise NotImplementedError
-
end
-
-
1
def set(value)
-
raise NotImplementedError
-
end
-
-
1
def select_option
-
raise NotImplementedError
-
end
-
-
1
def unselect_option
-
raise NotImplementedError
-
end
-
-
1
def click
-
raise NotImplementedError
-
end
-
-
1
def drag_to(element)
-
raise NotImplementedError
-
end
-
-
1
def tag_name
-
raise NotImplementedError
-
end
-
-
1
def visible?
-
raise NotImplementedError
-
end
-
-
1
def checked?
-
raise NotImplementedError
-
end
-
-
1
def selected?
-
raise NotImplementedError
-
end
-
-
1
def path
-
raise NotSupportedByDriverError
-
end
-
-
1
def trigger(event)
-
raise NotSupportedByDriverError
-
end
-
-
1
def inspect
-
%(#<Capybara::Driver::Node tag="#{tag_name}" path="#{path}">)
-
rescue NotSupportedByDriverError
-
%(#<Capybara::Driver::Node tag="#{tag_name}">)
-
end
-
end
-
end
-
end
-
1
require 'capybara'
-
-
1
module Capybara
-
1
def self.included(base)
-
base.send(:include, Capybara::DSL)
-
warn "`include Capybara` is deprecated please use `include Capybara::DSL` instead."
-
end
-
-
1
class << self
-
1
attr_writer :default_driver, :current_driver, :javascript_driver, :session_name
-
-
1
attr_accessor :app
-
-
##
-
#
-
# @return [Symbol] The name of the driver to use by default
-
#
-
1
def default_driver
-
@default_driver || :rack_test
-
end
-
-
##
-
#
-
# @return [Symbol] The name of the driver currently in use
-
#
-
1
def current_driver
-
@current_driver || default_driver
-
end
-
1
alias_method :mode, :current_driver
-
-
##
-
#
-
# @return [Symbol] The name of the driver used when JavaScript is needed
-
#
-
1
def javascript_driver
-
@javascript_driver || :selenium
-
end
-
-
##
-
#
-
# Use the default driver as the current driver
-
#
-
1
def use_default_driver
-
@current_driver = nil
-
end
-
-
##
-
#
-
# Yield a block using a specific driver
-
#
-
1
def using_driver(driver)
-
previous_driver = Capybara.current_driver
-
Capybara.current_driver = driver
-
yield
-
ensure
-
@current_driver = previous_driver
-
end
-
-
##
-
#
-
# Yield a block using a specific wait time
-
#
-
1
def using_wait_time(seconds)
-
previous_wait_time = Capybara.default_wait_time
-
Capybara.default_wait_time = seconds
-
yield
-
ensure
-
Capybara.default_wait_time = previous_wait_time
-
end
-
-
##
-
#
-
# The current Capybara::Session base on what is set as Capybara.app and Capybara.current_driver
-
#
-
# @return [Capybara::Session] The currently used session
-
#
-
1
def current_session
-
session_pool["#{current_driver}:#{session_name}:#{app.object_id}"] ||= Capybara::Session.new(current_driver, app)
-
end
-
-
##
-
#
-
# Reset sessions, cleaning out the pool of sessions. This will remove any session information such
-
# as cookies.
-
#
-
1
def reset_sessions!
-
session_pool.each { |mode, session| session.reset! }
-
end
-
1
alias_method :reset!, :reset_sessions!
-
-
##
-
#
-
# The current session name.
-
#
-
# @return [Symbol] The name of the currently used session.
-
#
-
1
def session_name
-
@session_name ||= :default
-
end
-
-
##
-
#
-
# Yield a block using a specific session name.
-
#
-
1
def using_session(name)
-
self.session_name = name
-
yield
-
ensure
-
self.session_name = :default
-
end
-
-
1
private
-
-
1
def session_pool
-
@session_pool ||= {}
-
end
-
end
-
-
1
module DSL
-
-
##
-
#
-
# Shortcut to working in a different session. This is useful when Capybara is included
-
# in a class or module.
-
#
-
1
def using_session(name, &block)
-
Capybara.using_session(name, &block)
-
end
-
-
##
-
#
-
# Shortcut to working in a different session. This is useful when Capybara is included
-
# in a class or module.
-
#
-
1
def using_wait_time(seconds, &block)
-
Capybara.using_wait_time(seconds, &block)
-
end
-
-
##
-
#
-
# Shortcut to accessing the current session. This is useful when Capybara is included in a
-
# class or module.
-
#
-
# class MyClass
-
# include Capybara::DSL
-
#
-
# def has_header?
-
# page.has_css?('h1')
-
# end
-
# end
-
#
-
# @return [Capybara::Session] The current session object
-
#
-
1
def page
-
Capybara.current_session
-
end
-
-
1
Session::DSL_METHODS.each do |method|
-
59
class_eval <<-RUBY, __FILE__, __LINE__+1
-
def #{method}(*args, &block)
-
page.#{method}(*args, &block)
-
end
-
RUBY
-
end
-
end
-
-
1
extend(Capybara::DSL)
-
end
-
1
class Capybara::RackTest::Node < Capybara::Driver::Node
-
1
def text
-
native.text
-
end
-
-
1
def [](name)
-
string_node[name]
-
end
-
-
1
def value
-
string_node.value
-
end
-
-
1
def set(value)
-
if tag_name == 'input' and type == 'radio'
-
other_radios_xpath = XPath.generate { |x| x.anywhere(:input)[x.attr(:name).equals(self[:name])] }.to_s
-
driver.dom.xpath(other_radios_xpath).each { |node| node.remove_attribute("checked") }
-
native['checked'] = 'checked'
-
elsif tag_name == 'input' and type == 'checkbox'
-
if value && !native['checked']
-
native['checked'] = 'checked'
-
elsif !value && native['checked']
-
native.remove_attribute('checked')
-
end
-
elsif tag_name == 'input'
-
if (type == 'text' || type == 'password') && self[:maxlength] &&
-
!self[:maxlength].empty?
-
# Browser behavior for maxlength="0" is inconsistent, so we stick with
-
# Firefox, allowing no input
-
value = value[0...self[:maxlength].to_i]
-
end
-
native['value'] = value.to_s
-
elsif tag_name == "textarea"
-
native.content = value.to_s
-
end
-
end
-
-
1
def select_option
-
if select_node['multiple'] != 'multiple'
-
select_node.find(".//option[@selected]").each { |node| node.native.remove_attribute("selected") }
-
end
-
native["selected"] = 'selected'
-
end
-
-
1
def unselect_option
-
if select_node['multiple'] != 'multiple'
-
raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box."
-
end
-
native.remove_attribute('selected')
-
end
-
-
1
def click
-
if tag_name == 'a'
-
method = self["data-method"] if driver.options[:respect_data_method]
-
method ||= :get
-
driver.follow(method, self[:href].to_s)
-
elsif (tag_name == 'input' and %w(submit image).include?(type)) or
-
((tag_name == 'button') and type.nil? or type == "submit")
-
Capybara::RackTest::Form.new(driver, form).submit(self)
-
end
-
end
-
-
1
def tag_name
-
native.node_name
-
end
-
-
1
def visible?
-
string_node.visible?
-
end
-
-
1
def checked?
-
string_node.checked?
-
end
-
-
1
def selected?
-
string_node.selected?
-
end
-
-
1
def path
-
native.path
-
end
-
-
1
def find(locator)
-
native.xpath(locator).map { |n| self.class.new(driver, n) }
-
end
-
-
1
private
-
-
1
def string_node
-
@string_node ||= Capybara::Node::Simple.new(native)
-
end
-
-
# a reference to the select node if this is an option node
-
1
def select_node
-
find('./ancestor::select').first
-
end
-
-
1
def type
-
native[:type]
-
end
-
-
1
def form
-
native.ancestors('form').first
-
end
-
end
-
1
require 'capybara'
-
1
require 'capybara/dsl'
-
-
1
Capybara.app = Rack::Builder.new do
-
1
map "/" do
-
1
if Rails.version.to_f >= 3.0
-
1
run Rails.application
-
else # Rails 2
-
use Rails::Rack::Static
-
run ActionController::Dispatcher.new
-
end
-
end
-
end.to_app
-
-
1
Capybara.asset_root = Rails.root.join('public')
-
1
Capybara.save_and_open_page_path = Rails.root.join('tmp/capybara')
-
-
1
module Capybara
-
1
module RSpecMatchers
-
1
class HaveSelector
-
1
def initialize(*args)
-
@args = args
-
end
-
-
1
def matches?(actual)
-
@actual = wrap(actual)
-
@actual.has_selector?(*@args)
-
end
-
-
1
def does_not_match?(actual)
-
@actual = wrap(actual)
-
@actual.has_no_selector?(*@args)
-
end
-
-
1
def failure_message_for_should
-
if normalized.failure_message
-
normalized.failure_message.call(@actual, normalized)
-
else
-
"expected #{selector_name} to return something"
-
end
-
end
-
-
1
def failure_message_for_should_not
-
"expected #{selector_name} not to return anything"
-
end
-
-
1
def description
-
"has #{selector_name}"
-
end
-
-
1
def selector_name
-
name = "#{normalized.name} #{normalized.locator.inspect}"
-
name << " with text #{normalized.options[:text].inspect}" if normalized.options[:text]
-
name
-
end
-
-
1
def wrap(actual)
-
if actual.respond_to?("has_selector?")
-
actual
-
else
-
Capybara.string(actual.to_s)
-
end
-
end
-
-
1
def normalized
-
@normalized ||= Capybara::Selector.normalize(*@args)
-
end
-
end
-
-
1
class HaveMatcher
-
1
attr_reader :name, :locator, :options, :failure_message, :actual
-
-
1
def initialize(name, locator, options={}, &block)
-
@name = name
-
@locator = locator
-
@options = options
-
@failure_message = block
-
end
-
-
1
def arguments
-
if options.empty? then [locator] else [locator, options] end
-
end
-
-
1
def matches?(actual)
-
@actual = wrap(actual)
-
@actual.send(:"has_#{name}?", *arguments)
-
end
-
-
1
def does_not_match?(actual)
-
@actual = wrap(actual)
-
@actual.send(:"has_no_#{name}?", *arguments)
-
end
-
-
1
def failure_message_for_should
-
if failure_message
-
failure_message.call(actual, self)
-
else
-
"expected #{selector_name} to return something"
-
end
-
end
-
-
1
def failure_message_for_should_not
-
"expected #{selector_name} not to return anything"
-
end
-
-
1
def description
-
"has #{selector_name}"
-
end
-
-
1
def selector_name
-
selector_name = "#{name} #{locator.inspect}"
-
selector_name << " with text #{options[:text].inspect}" if options[:text]
-
selector_name
-
end
-
-
1
def wrap(actual)
-
if actual.respond_to?("has_selector?")
-
actual
-
else
-
Capybara.string(actual.to_s)
-
end
-
end
-
end
-
-
1
def have_selector(*args)
-
HaveSelector.new(*args)
-
end
-
-
1
def have_xpath(xpath, options={})
-
HaveMatcher.new(:xpath, xpath, options)
-
end
-
-
1
def have_css(css, options={})
-
HaveMatcher.new(:css, css, options)
-
end
-
-
1
def have_content(text)
-
HaveMatcher.new(:content, text.to_s) do |page, matcher|
-
%(expected there to be content #{matcher.locator.inspect} in #{page.text.inspect})
-
end
-
end
-
-
1
def have_link(locator, options={})
-
HaveMatcher.new(:link, locator, options)
-
end
-
-
1
def have_button(locator)
-
HaveMatcher.new(:button, locator)
-
end
-
-
1
def have_field(locator, options={})
-
HaveMatcher.new(:field, locator, options)
-
end
-
-
1
def have_checked_field(locator)
-
HaveMatcher.new(:checked_field, locator)
-
end
-
-
1
def have_unchecked_field(locator)
-
HaveMatcher.new(:unchecked_field, locator)
-
end
-
-
1
def have_select(locator, options={})
-
HaveMatcher.new(:select, locator, options)
-
end
-
-
1
def have_table(locator, options={})
-
HaveMatcher.new(:table, locator, options)
-
end
-
end
-
end
-
1
require 'capybara/util/timeout'
-
-
1
module Capybara
-
-
##
-
#
-
# The Session class represents a single user's interaction with the system. The Session can use
-
# any of the underlying drivers. A session can be initialized manually like this:
-
#
-
# session = Capybara::Session.new(:culerity, MyRackApp)
-
#
-
# The application given as the second argument is optional. When running Capybara against an external
-
# page, you might want to leave it out:
-
#
-
# session = Capybara::Session.new(:culerity)
-
# session.visit('http://www.google.com')
-
#
-
# Session provides a number of methods for controlling the navigation of the page, such as +visit+,
-
# +current_path, and so on. It also delegate a number of methods to a Capybara::Document, representing
-
# the current HTML document. This allows interaction:
-
#
-
# session.fill_in('q', :with => 'Capybara')
-
# session.click_button('Search')
-
# session.should have_content('Capybara')
-
#
-
# When using capybara/dsl, the Session is initialized automatically for you.
-
#
-
1
class Session
-
1
NODE_METHODS = [
-
:all, :first, :attach_file, :text, :check, :choose,
-
:click_link_or_button, :click_button, :click_link, :field_labeled,
-
:fill_in, :find, :find_button, :find_by_id, :find_field, :find_link,
-
:has_content?, :has_css?, :has_no_content?, :has_no_css?, :has_no_xpath?,
-
:has_xpath?, :select, :uncheck, :has_link?, :has_no_link?, :has_button?,
-
:has_no_button?, :has_field?, :has_no_field?, :has_checked_field?,
-
:has_unchecked_field?, :has_no_table?, :has_table?, :unselect,
-
:has_select?, :has_no_select?, :has_selector?, :has_no_selector?,
-
:click_on, :has_no_checked_field?, :has_no_unchecked_field?
-
]
-
1
SESSION_METHODS = [
-
:body, :html, :current_url, :current_host, :evaluate_script, :source,
-
:visit, :wait_until, :within, :within_fieldset, :within_table,
-
:within_frame, :within_window, :current_path, :save_page,
-
:save_and_open_page, :reset_session!
-
]
-
1
DSL_METHODS = NODE_METHODS + SESSION_METHODS
-
-
1
attr_reader :mode, :app
-
-
1
def initialize(mode, app=nil)
-
@mode = mode
-
@app = app
-
end
-
-
1
def driver
-
@driver ||= begin
-
unless Capybara.drivers.has_key?(mode)
-
other_drivers = Capybara.drivers.keys.map { |key| key.inspect }
-
raise Capybara::DriverNotFoundError, "no driver called #{mode.inspect} was found, available drivers: #{other_drivers.join(', ')}"
-
end
-
Capybara.drivers[mode].call(app)
-
end
-
end
-
-
##
-
#
-
# Reset the session, removing all cookies.
-
#
-
1
def reset!
-
driver.reset!
-
end
-
1
alias_method :cleanup!, :reset!
-
1
alias_method :reset_session!, :reset!
-
-
##
-
#
-
# Returns a hash of response headers. Not supported by all drivers (e.g. Selenium)
-
#
-
# @return [Hash{String => String}] A hash of response headers.
-
#
-
1
def response_headers
-
driver.response_headers
-
end
-
-
##
-
#
-
# Returns the current HTTP status code as an Integer. Not supported by all drivers (e.g. Selenium)
-
#
-
# @return [Integer] Current HTTP status code
-
#
-
1
def status_code
-
driver.status_code
-
end
-
-
##
-
#
-
# @return [String] A snapshot of the HTML of the current document, as it looks right now (potentially modified by JavaScript).
-
#
-
1
def body
-
driver.body
-
end
-
1
alias_method :html, :body
-
-
##
-
#
-
# @return [String] HTML source of the document, before being modified by JavaScript.
-
#
-
1
def source
-
driver.source
-
end
-
-
##
-
#
-
# @return [String] Path of the current page, without any domain information
-
#
-
1
def current_path
-
path = URI.parse(current_url).path
-
path if path and not path.empty?
-
end
-
-
##
-
#
-
# @return [String] Host of the current page
-
#
-
1
def current_host
-
uri = URI.parse(current_url)
-
"#{uri.scheme}://#{uri.host}" if uri.host
-
end
-
-
##
-
#
-
# @return [String] Fully qualified URL of the current page
-
#
-
1
def current_url
-
driver.current_url
-
end
-
-
##
-
#
-
# Navigate to the given URL. The URL can either be a relative URL or an absolute URL
-
# The behaviour of either depends on the driver.
-
#
-
# session.visit('/foo')
-
# session.visit('http://google.com')
-
#
-
# For drivers which can run against an external application, such as culerity and selenium
-
# giving an absolute URL will navigate to that page. This allows testing applications
-
# running on remote servers. For these drivers, setting Capybara.app_host will make the
-
# remote server the default. For example:
-
#
-
# Capybara.app_host = 'http://google.com'
-
# session.visit('/') # visits the google homepage
-
#
-
# @param [String] url The URL to navigate to
-
#
-
1
def visit(url)
-
driver.visit(url)
-
end
-
-
##
-
#
-
# Execute the given block for a particular scope on the page. Within will find the first
-
# element matching the given selector and execute the block scoped to that element:
-
#
-
# within(:xpath, '//div[@id="delivery-address"]') do
-
# fill_in('Street', :with => '12 Main Street')
-
# end
-
#
-
# It is possible to omit the first parameter, in that case, the selector is assumed to be
-
# of the type set in Capybara.default_selector.
-
#
-
# within('div#delivery-address') do
-
# fill_in('Street', :with => '12 Main Street')
-
# end
-
#
-
# @overload within(*find_args)
-
# @param (see Capybara::Node::Finders#all)
-
#
-
# @overload within(a_node)
-
# @param [Capybara::Node::Base] a_node The node in whose scope the block should be evaluated
-
#
-
# @raise [Capybara::ElementNotFound] If the scope can't be found before time expires
-
#
-
1
def within(*args)
-
new_scope = if args.size == 1 && Capybara::Node::Base === args.first
-
args.first
-
else
-
find(*args)
-
end
-
begin
-
scopes.push(new_scope)
-
yield
-
ensure
-
scopes.pop
-
end
-
end
-
-
##
-
#
-
# Execute the given block within the a specific fieldset given the id or legend of that fieldset.
-
#
-
# @param [String] locator Id or legend of the fieldset
-
#
-
1
def within_fieldset(locator)
-
within :xpath, XPath::HTML.fieldset(locator) do
-
yield
-
end
-
end
-
-
##
-
#
-
# Execute the given block within the a specific table given the id or caption of that table.
-
#
-
# @param [String] locator Id or caption of the table
-
#
-
1
def within_table(locator)
-
within :xpath, XPath::HTML.table(locator) do
-
yield
-
end
-
end
-
-
##
-
#
-
# Execute the given block within the given iframe given the id of that iframe. Only works on
-
# some drivers (e.g. Selenium)
-
#
-
# @param [String] locator Id of the frame
-
#
-
1
def within_frame(frame_id)
-
driver.within_frame(frame_id) do
-
yield
-
end
-
end
-
-
##
-
#
-
# Execute the given block within the given window. Only works on
-
# some drivers (e.g. Selenium)
-
#
-
# @param [String] locator of the window
-
#
-
1
def within_window(handle, &blk)
-
driver.within_window(handle, &blk)
-
end
-
-
##
-
#
-
# Retry executing the block until a truthy result is returned or the timeout time is exceeded
-
#
-
# @param [Integer] timeout The amount of seconds to retry executing the given block
-
#
-
1
def wait_until(timeout = Capybara.default_wait_time)
-
Capybara.timeout(timeout,driver) { yield }
-
end
-
-
##
-
#
-
# Execute the given script, not returning a result. This is useful for scripts that return
-
# complex objects, such as jQuery statements. +execute_script+ should always be used over
-
# +evaluate_script+ whenever possible.
-
#
-
# @param [String] script A string of JavaScript to execute
-
#
-
1
def execute_script(script)
-
driver.execute_script(script)
-
end
-
-
##
-
#
-
# Evaluate the given JavaScript and return the result. Be careful when using this with
-
# scripts that return complex objects, such as jQuery statements. +execute_script+ might
-
# be a better alternative.
-
#
-
# @param [String] script A string of JavaScript to evaluate
-
# @return [Object] The result of the evaluated JavaScript (may be driver specific)
-
#
-
1
def evaluate_script(script)
-
driver.evaluate_script(script)
-
end
-
-
##
-
#
-
# Save a snapshot of the page and open it in a browser for inspection
-
#
-
1
def save_page
-
require 'capybara/util/save_and_open_page'
-
Capybara.save_page(body)
-
end
-
-
1
def save_and_open_page
-
require 'capybara/util/save_and_open_page'
-
Capybara.save_and_open_page(body)
-
end
-
-
1
def document
-
@document ||= Capybara::Node::Document.new(self, driver)
-
end
-
-
1
NODE_METHODS.each do |method|
-
class_eval <<-RUBY
-
def #{method}(*args, &block)
-
current_node.send(:#{method}, *args, &block)
-
end
-
42
RUBY
-
end
-
-
1
def inspect
-
%(#<Capybara::Session>)
-
end
-
-
1
private
-
-
1
def current_node
-
scopes.last
-
end
-
-
1
def scopes
-
@scopes ||= [document]
-
end
-
end
-
end
-
1
module Capybara
-
1
class << self
-
-
##
-
# Provides timeout similar to standard library Timeout, but avoids threads
-
#
-
1
def timeout(seconds = 1, driver = nil, error_message = nil, &block)
-
start_time = Time.now
-
-
result = nil
-
-
until result
-
return result if result = yield
-
-
delay = seconds - (Time.now - start_time)
-
if delay <= 0
-
raise TimeoutError, error_message || "timed out"
-
end
-
-
driver && driver.wait_until(delay)
-
-
sleep(0.05)
-
end
-
end
-
-
end
-
end
-
1
require 'coffee-script'
-
1
require 'coffee/rails/railtie'
-
1
require 'coffee/rails/template_handler'
-
1
require 'coffee/rails/version'
-
1
require 'rails/railtie'
-
-
1
module Coffee
-
1
module Rails
-
1
class Railtie < ::Rails::Railtie
-
1
config.app_generators.javascript_engine :coffee
-
end
-
end
-
end
-
1
module Coffee
-
1
module Rails
-
1
class TemplateHandler
-
1
def self.erb_handler
-
@@erb_handler ||= ActionView::Template.registered_template_handler(:erb)
-
end
-
-
1
def self.call(template)
-
compiled_source = erb_handler.call(template)
-
"CoffeeScript.compile(begin;#{compiled_source};end)"
-
end
-
end
-
end
-
end
-
-
1
ActiveSupport.on_load(:action_view) do
-
1
ActionView::Template.register_template_handler :coffee, Coffee::Rails::TemplateHandler
-
end
-
1
module Coffee
-
1
module Rails
-
1
VERSION = "3.1.1"
-
end
-
end
-
1
require 'coffee_script'
-
1
require 'execjs'
-
1
require 'coffee_script/source'
-
-
1
module CoffeeScript
-
1
EngineError = ExecJS::RuntimeError
-
1
CompilationError = ExecJS::ProgramError
-
-
1
module Source
-
1
def self.path
-
@path ||= ENV['COFFEESCRIPT_SOURCE_PATH'] || bundled_path
-
end
-
-
1
def self.path=(path)
-
@contents = @version = @bare_option = @context = nil
-
@path = path
-
end
-
-
1
def self.contents
-
@contents ||= File.read(path)
-
end
-
-
1
def self.version
-
@version ||= contents[/CoffeeScript Compiler v([\d.]+)/, 1]
-
end
-
-
1
def self.bare_option
-
@bare_option ||= contents.match(/noWrap/) ? 'noWrap' : 'bare'
-
end
-
-
1
def self.context
-
@context ||= ExecJS.compile(contents)
-
end
-
end
-
-
1
class << self
-
1
def engine
-
end
-
-
1
def engine=(engine)
-
end
-
-
1
def version
-
Source.version
-
end
-
-
# Compile a script (String or IO) to JavaScript.
-
1
def compile(script, options = {})
-
script = script.read if script.respond_to?(:read)
-
-
if options.key?(:bare)
-
elsif options.key?(:no_wrap)
-
options[:bare] = options[:no_wrap]
-
else
-
options[:bare] = false
-
end
-
-
Source.context.call("CoffeeScript.compile", script, options)
-
end
-
end
-
end
-
1
module CoffeeScript
-
1
module Source
-
1
def self.bundled_path
-
File.expand_path("../coffee-script.js", __FILE__)
-
end
-
end
-
end
-
1
require 'cucumber/formatter/ansicolor'
-
1
require 'cucumber/formatter/duration'
-
1
require 'cucumber/formatter/summary'
-
-
1
module Cucumber
-
1
module Formatter
-
# This module contains helper methods that are used by formatters
-
# that print output to the terminal.
-
1
module Console
-
1
extend ANSIColor
-
1
include Duration
-
1
include Summary
-
-
1
FORMATS = Hash.new{|hash, format| hash[format] = method(format).to_proc}
-
-
1
def format_step(keyword, step_match, status, source_indent)
-
comment = if source_indent
-
c = (' # ' + step_match.file_colon_line).indent(source_indent)
-
format_string(c, :comment)
-
else
-
''
-
end
-
-
format = format_for(status, :param)
-
line = keyword + step_match.format_args(format) + comment
-
format_string(line, status)
-
end
-
-
1
def format_string(o, status)
-
fmt = format_for(status)
-
o.to_s.split("\n").map do |line|
-
if Proc === fmt
-
fmt.call(line)
-
else
-
fmt % line
-
end
-
end.join("\n")
-
end
-
-
1
def print_steps(status)
-
print_elements(step_mother.steps(status), status, 'steps')
-
end
-
-
1
def print_elements(elements, status, kind)
-
if elements.any?
-
@io.puts(format_string("(::) #{status} #{kind} (::)", status))
-
@io.puts
-
@io.flush
-
end
-
-
elements.each_with_index do |element, i|
-
if status == :failed
-
print_exception(element.exception, status, 0)
-
else
-
@io.puts(format_string(element.backtrace_line, status))
-
end
-
@io.puts
-
@io.flush
-
end
-
end
-
-
1
def print_stats(features, options)
-
@failures = step_mother.scenarios(:failed).select { |s| s.is_a?(Cucumber::Ast::Scenario) || s.is_a?(Cucumber::Ast::OutlineTable::ExampleRow) }
-
@failures.collect! { |s| (s.is_a?(Cucumber::Ast::OutlineTable::ExampleRow)) ? s.scenario_outline : s }
-
-
if !@failures.empty?
-
@io.puts format_string("Failing Scenarios:", :failed)
-
@failures.each do |failure|
-
profiles_string = options.custom_profiles.empty? ? '' : (options.custom_profiles.map{|profile| "-p #{profile}" }).join(' ') + ' '
-
source = options[:source] ? format_string(" # Scenario: " + failure.name, :comment) : ''
-
@io.puts format_string("cucumber #{profiles_string}" + failure.file_colon_line, :failed) + source
-
end
-
@io.puts
-
end
-
-
@io.puts scenario_summary(step_mother) {|status_count, status| format_string(status_count, status)}
-
@io.puts step_summary(step_mother) {|status_count, status| format_string(status_count, status)}
-
-
@io.puts(format_duration(features.duration)) if features && features.duration
-
-
@io.flush
-
end
-
-
1
def print_exception(e, status, indent)
-
message = "#{e.message} (#{e.class})"
-
if ENV['CUCUMBER_TRUNCATE_OUTPUT']
-
message = linebreaks(message, ENV['CUCUMBER_TRUNCATE_OUTPUT'].to_i)
-
end
-
-
string = "#{message}\n#{e.backtrace.join("\n")}".indent(indent)
-
@io.puts(format_string(string, status))
-
end
-
-
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/10655
-
1
def linebreaks(s, max)
-
s.gsub(/.{1,#{max}}(?:\s|\Z)/){($& + 5.chr).gsub(/\n\005/,"\n").gsub(/\005/,"\n")}.rstrip
-
end
-
-
1
def print_snippets(options)
-
return unless options[:snippets]
-
undefined = step_mother.steps(:undefined)
-
return if undefined.empty?
-
-
unknown_programming_language = step_mother.unknown_programming_language?
-
snippets = undefined.map do |step|
-
step_name = Undefined === step.exception ? step.exception.step_name : step.name
-
step_multiline_class = step.multiline_arg ? step.multiline_arg.class : nil
-
snippet = @step_mother.snippet_text(step.actual_keyword, step_name, step_multiline_class)
-
snippet
-
end.compact.uniq
-
-
text = "\nYou can implement step definitions for undefined steps with these snippets:\n\n"
-
text += snippets.join("\n\n")
-
@io.puts format_string(text, :undefined)
-
-
if unknown_programming_language
-
@io.puts format_string("\nIf you want snippets in a different programming language,\n" +
-
"just make sure a file with the appropriate file extension\n" +
-
"exists where cucumber looks for step definitions.", :failed)
-
end
-
-
@io.puts
-
@io.flush
-
end
-
-
1
def print_passing_wip(options)
-
return unless options[:wip]
-
passed = step_mother.scenarios(:passed)
-
if passed.any?
-
@io.puts format_string("\nThe --wip switch was used, so I didn't expect anything to pass. These scenarios passed:", :failed)
-
print_elements(passed, :passed, "scenarios")
-
else
-
@io.puts format_string("\nThe --wip switch was used, so the failures were expected. All is good.\n", :passed)
-
end
-
end
-
-
1
def embed(file, mime_type, label)
-
# no-op
-
end
-
-
#define @delayed_messages = [] in your Formatter if you want to
-
#activate this feature
-
1
def puts(*messages)
-
if @delayed_messages
-
@delayed_messages += messages
-
else
-
if @io
-
@io.puts
-
messages.each do |message|
-
@io.puts(format_string(message, :tag))
-
end
-
@io.flush
-
end
-
end
-
end
-
-
1
def print_messages
-
@delayed_messages.each {|message| print_message(message)}
-
empty_messages
-
end
-
-
1
def print_table_row_messages
-
return if @delayed_messages.empty?
-
@io.print(format_string(@delayed_messages.join(', '), :tag).indent(2))
-
@io.flush
-
empty_messages
-
end
-
-
1
def print_message(message)
-
@io.puts(format_string(message, :tag).indent(@indent))
-
@io.flush
-
end
-
-
1
def empty_messages
-
@delayed_messages = []
-
end
-
-
1
private
-
-
1
def format_for(*keys)
-
key = keys.join('_').to_sym
-
fmt = FORMATS[key]
-
raise "No format for #{key.inspect}: #{FORMATS.inspect}" if fmt.nil?
-
fmt
-
end
-
end
-
end
-
end
-
1
module Cucumber
-
1
module Formatter
-
1
module Io
-
1
def ensure_io(path_or_io, name)
-
1
return nil if path_or_io.nil?
-
1
return path_or_io if path_or_io.respond_to?(:write)
-
file = File.open(path_or_io, Cucumber.file_mode('w'))
-
at_exit do
-
unless file.closed?
-
file.flush
-
file.close
-
end
-
end
-
file
-
end
-
-
1
def ensure_file(path, name)
-
raise "You *must* specify --out FILE for the #{name} formatter" unless String === path
-
raise "I can't write #{name} to a directory - it has to be a file" if File.directory?(path)
-
ensure_io(path, name)
-
end
-
-
1
def ensure_dir(path, name)
-
raise "You *must* specify --out DIR for the #{name} formatter" unless String === path
-
raise "I can't write #{name} reports to a file - it has to be a directory" if File.file?(path)
-
FileUtils.mkdir_p(path) unless File.directory?(path)
-
path
-
end
-
end
-
end
-
end
-
1
require 'fileutils'
-
1
require 'cucumber/formatter/console'
-
1
require 'cucumber/formatter/io'
-
1
require 'gherkin/formatter/escaping'
-
-
1
module Cucumber
-
1
module Formatter
-
# The formatter used for <tt>--format pretty</tt> (the default formatter).
-
#
-
# This formatter prints features to plain text - exactly how they were parsed,
-
# just prettier. That means with proper indentation and alignment of table columns.
-
#
-
# If the output is STDOUT (and not a file), there are bright colours to watch too.
-
#
-
1
class Pretty
-
1
include FileUtils
-
1
include Console
-
1
include Io
-
1
include Gherkin::Formatter::Escaping
-
1
attr_writer :indent
-
1
attr_reader :step_mother
-
-
1
def initialize(step_mother, path_or_io, options)
-
1
@step_mother, @io, @options = step_mother, ensure_io(path_or_io, "pretty"), options
-
1
@exceptions = []
-
1
@indent = 0
-
1
@prefixes = options[:prefixes] || {}
-
1
@delayed_messages = []
-
end
-
-
1
def after_features(features)
-
print_summary(features) unless @options[:autoformat]
-
end
-
-
1
def before_feature(feature)
-
@exceptions = []
-
@indent = 0
-
if @options[:autoformat]
-
file = File.join(@options[:autoformat], feature.file)
-
dir = File.dirname(file)
-
mkdir_p(dir) unless File.directory?(dir)
-
@io = ensure_file(file, "pretty")
-
end
-
end
-
-
1
def comment_line(comment_line)
-
@io.puts(comment_line.indent(@indent))
-
@io.flush
-
end
-
-
1
def after_tags(tags)
-
if @indent == 1
-
@io.puts
-
@io.flush
-
end
-
end
-
-
1
def tag_name(tag_name)
-
tag = format_string(tag_name, :tag).indent(@indent)
-
@io.print(tag)
-
@io.flush
-
@indent = 1
-
end
-
-
1
def feature_name(keyword, name)
-
@io.puts("#{keyword}: #{name}")
-
@io.puts
-
@io.flush
-
end
-
-
1
def before_feature_element(feature_element)
-
@indent = 2
-
@scenario_indent = 2
-
end
-
-
1
def after_feature_element(feature_element)
-
@io.puts
-
@io.flush
-
end
-
-
1
def before_background(background)
-
@indent = 2
-
@scenario_indent = 2
-
@in_background = true
-
end
-
-
1
def after_background(background)
-
@in_background = nil
-
@io.puts
-
@io.flush
-
end
-
-
1
def background_name(keyword, name, file_colon_line, source_indent)
-
print_feature_element_name(keyword, name, file_colon_line, source_indent)
-
end
-
-
1
def before_examples_array(examples_array)
-
@indent = 4
-
@io.puts
-
@visiting_first_example_name = true
-
end
-
-
1
def examples_name(keyword, name)
-
@io.puts unless @visiting_first_example_name
-
@visiting_first_example_name = false
-
names = name.strip.empty? ? [name.strip] : name.split("\n")
-
@io.puts(" #{keyword}: #{names[0]}")
-
names[1..-1].each {|s| @io.puts " #{s}" } unless names.empty?
-
@io.flush
-
@indent = 6
-
@scenario_indent = 6
-
end
-
-
1
def before_outline_table(outline_table)
-
@table = outline_table
-
end
-
-
1
def after_outline_table(outline_table)
-
@table = nil
-
@indent = 4
-
end
-
-
1
def scenario_name(keyword, name, file_colon_line, source_indent)
-
print_feature_element_name(keyword, name, file_colon_line, source_indent)
-
end
-
-
1
def before_step(step)
-
@current_step = step
-
@indent = 6
-
end
-
-
1
def before_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
-
@hide_this_step = false
-
if exception
-
if @exceptions.include?(exception)
-
@hide_this_step = true
-
return
-
end
-
@exceptions << exception
-
end
-
if status != :failed && @in_background ^ background
-
@hide_this_step = true
-
return
-
end
-
@status = status
-
end
-
-
1
def step_name(keyword, step_match, status, source_indent, background)
-
return if @hide_this_step
-
source_indent = nil unless @options[:source]
-
name_to_report = format_step(keyword, step_match, status, source_indent)
-
@io.puts(name_to_report.indent(@scenario_indent + 2))
-
print_messages
-
end
-
-
1
def doc_string(string)
-
return if @hide_this_step
-
s = %{"""\n#{string}\n"""}.indent(@indent)
-
s = s.split("\n").map{|l| l =~ /^\s+$/ ? '' : l}.join("\n")
-
@io.puts(format_string(s, @current_step.status))
-
@io.flush
-
end
-
-
1
def exception(exception, status)
-
return if @hide_this_step
-
print_exception(exception, status, @indent)
-
@io.flush
-
end
-
-
1
def before_multiline_arg(multiline_arg)
-
return if @options[:no_multiline] || @hide_this_step
-
@table = multiline_arg
-
end
-
-
1
def after_multiline_arg(multiline_arg)
-
@table = nil
-
end
-
-
1
def before_table_row(table_row)
-
return if !@table || @hide_this_step
-
@col_index = 0
-
@io.print ' |'.indent(@indent-2)
-
end
-
-
1
def after_table_row(table_row)
-
return if !@table || @hide_this_step
-
print_table_row_messages
-
@io.puts
-
if table_row.exception && !@exceptions.include?(table_row.exception)
-
print_exception(table_row.exception, table_row.status, @indent)
-
end
-
end
-
-
1
def after_table_cell(cell)
-
return unless @table
-
@col_index += 1
-
end
-
-
1
def table_cell_value(value, status)
-
return if !@table || @hide_this_step
-
status ||= @status || :passed
-
width = @table.col_width(@col_index)
-
cell_text = escape_cell(value.to_s || '')
-
padded = cell_text + (' ' * (width - cell_text.unpack('U*').length))
-
prefix = cell_prefix(status)
-
@io.print(' ' + format_string("#{prefix}#{padded}", status) + ::Term::ANSIColor.reset(" |"))
-
@io.flush
-
end
-
-
1
private
-
-
1
def print_feature_element_name(keyword, name, file_colon_line, source_indent)
-
@io.puts if @scenario_indent == 6
-
names = name.empty? ? [name] : name.split("\n")
-
line = "#{keyword}: #{names[0]}".indent(@scenario_indent)
-
@io.print(line)
-
if @options[:source]
-
line_comment = " # #{file_colon_line}".indent(source_indent)
-
@io.print(format_string(line_comment, :comment))
-
end
-
@io.puts
-
names[1..-1].each {|s| @io.puts " #{s}"}
-
@io.flush
-
end
-
-
1
def cell_prefix(status)
-
@prefixes[status]
-
end
-
-
1
def print_summary(features)
-
print_stats(features, @options)
-
print_snippets(@options)
-
print_passing_wip(@options)
-
end
-
end
-
end
-
end
-
1
module Cucumber
-
1
module Formatter
-
1
module Summary
-
-
1
def scenario_summary(step_mother, &block)
-
scenarios_proc = lambda{|status| step_mother.scenarios(status)}
-
dump_count(step_mother.scenarios.length, "scenario") + dump_status_counts(scenarios_proc, &block)
-
end
-
-
1
def step_summary(step_mother, &block)
-
steps_proc = lambda{|status| step_mother.steps(status)}
-
dump_count(step_mother.steps.length, "step") + dump_status_counts(steps_proc, &block)
-
end
-
-
1
private
-
-
1
def dump_status_counts(find_elements_proc)
-
counts = [:failed, :skipped, :undefined, :pending, :passed].map do |status|
-
elements = find_elements_proc.call(status)
-
elements.any? ? yield("#{elements.length} #{status.to_s}", status) : nil
-
end.compact
-
if counts.any?
-
" (#{counts.join(', ')})"
-
else
-
""
-
end
-
end
-
-
1
def dump_count(count, what, state=nil)
-
[count, state, "#{what}#{count == 1 ? '' : 's'}"].compact.join(" ")
-
end
-
-
end
-
end
-
end
-
1
ActionController::Base.class_eval do
-
1
cattr_accessor :allow_rescue
-
end
-
-
1
class ActionDispatch::ShowExceptions
-
1
alias __cucumber_orig_call__ call
-
-
1
def call(env)
-
env['action_dispatch.show_exceptions'] = !!ActionController::Base.allow_rescue
-
__cucumber_orig_call__(env)
-
end
-
end
-
1
require 'capybara'
-
1
require 'capybara/rails'
-
1
require 'capybara/cucumber'
-
1
require 'capybara/session'
-
1
require 'cucumber/rails/capybara/javascript_emulation'
-
1
require 'cucumber/rails/capybara/select_dates_and_times'
-
1
module Cucumber
-
1
module Rails
-
1
module Capybara
-
1
module JavascriptEmulation
-
1
def self.included(base)
-
1
base.class_eval do
-
1
alias_method :click_without_javascript_emulation, :click
-
1
alias_method :click, :click_with_javascript_emulation
-
end
-
end
-
-
1
def click_with_javascript_emulation
-
if link_with_non_get_http_method?
-
::Capybara::RackTest::Form.new(driver, js_form(element_node.document, self[:href], emulated_method)).submit(self)
-
else
-
click_without_javascript_emulation
-
end
-
end
-
-
1
private
-
-
1
def csrf?
-
csrf_param_node && csrf_token_node
-
end
-
-
1
def csrf_param_node
-
element_node.document.at_xpath("//meta[@name='csrf-param']")
-
end
-
-
1
def csrf_param
-
csrf_param_node['content']
-
end
-
-
1
def csrf_token_node
-
element_node.document.at_xpath("//meta[@name='csrf-token']")
-
end
-
-
1
def csrf_token
-
csrf_token_node['content']
-
end
-
-
1
def js_form(document, action, emulated_method, method = 'POST')
-
js_form = document.create_element('form')
-
js_form['action'] = action
-
js_form['method'] = method
-
-
if emulated_method and emulated_method.downcase != method.downcase
-
input = document.create_element('input')
-
input['type'] = 'hidden'
-
input['name'] = '_method'
-
input['value'] = emulated_method
-
js_form.add_child(input)
-
end
-
-
# rails will wipe the session if the CSRF token is not sent
-
# with non-GET requests
-
if csrf? && emulated_method.downcase != "get"
-
input = document.create_element('input')
-
input['type'] = 'hidden'
-
input['name'] = csrf_param
-
input['value'] = csrf_token
-
js_form.add_child(input)
-
end
-
-
js_form
-
end
-
-
1
def link_with_non_get_http_method?
-
if ::Rails.version.to_f >= 3.0
-
tag_name == 'a' && element_node['data-method'] && element_node['data-method'] =~ /(?:delete|put|post)/
-
else
-
tag_name == 'a' && element_node['onclick'] && element_node['onclick'] =~ /var f = document\.createElement\('form'\); f\.style\.display = 'none';/
-
end
-
end
-
-
1
def emulated_method
-
if ::Rails.version.to_f >= 3.0
-
element_node['data-method']
-
else
-
element_node['onclick'][/m\.setAttribute\('value', '([^']*)'\)/, 1]
-
end
-
end
-
-
1
def element_node
-
if self.respond_to? :native
-
self.native
-
else
-
warn "DEPRECATED: cucumber-rails loves you, just not your version of Capybara. Please update Capybara to >= 0.4.0"
-
self.node
-
end
-
end
-
end
-
end
-
end
-
end
-
-
1
class Capybara::RackTest::Node
-
1
include ::Cucumber::Rails::Capybara::JavascriptEmulation
-
end
-
-
1
Before('~@no-js-emulation') do
-
# Enable javascript emulation
-
::Capybara::RackTest::Node.class_eval do
-
alias_method :click, :click_with_javascript_emulation
-
end
-
end
-
-
1
Before('@no-js-emulation') do
-
# Disable javascript emulation
-
::Capybara::RackTest::Node.class_eval do
-
alias_method :click, :click_without_javascript_emulation
-
end
-
end
-
1
module Cucumber
-
1
module Rails
-
1
module Capybara
-
# This module defines methods for selecting dates and times
-
1
module SelectDatesAndTimes
-
# Select a Rails date. Options has must include :from => +label+
-
1
def select_date(date, options)
-
date = Date.parse(date)
-
base_dom_id = get_base_dom_id_from_label_tag(options[:from])
-
-
find(:xpath, ".//select[@id='#{base_dom_id}_1i']").select(date.year.to_s)
-
find(:xpath, ".//select[@id='#{base_dom_id}_2i']").select(I18n.l date, :format => '%B')
-
find(:xpath, ".//select[@id='#{base_dom_id}_3i']").select(date.day.to_s)
-
end
-
-
# Select a Rails time. Options has must include :from => +label+
-
1
def select_time(time, options)
-
time = Time.zone.parse(time)
-
base_dom_id = get_base_dom_id_from_label_tag(options[:from])
-
-
find(:xpath, ".//select[@id='#{base_dom_id}_4i']").select(time.hour.to_s.rjust(2, '0'))
-
find(:xpath, ".//select[@id='#{base_dom_id}_5i']").select(time.min.to_s.rjust(2, '0'))
-
end
-
-
# Select a Rails datetime. Options has must include :from => +label+
-
1
def select_datetime(datetime, options)
-
select_date(datetime, options)
-
select_time(datetime, options)
-
end
-
-
1
private
-
-
# @example "event_starts_at_"
-
1
def get_base_dom_id_from_label_tag(field)
-
find(:xpath, ".//label[contains(., '#{field}')]")['for'].gsub(/(_[1-5]i)$/, '')
-
end
-
end
-
end
-
end
-
end
-
-
1
World(::Cucumber::Rails::Capybara::SelectDatesAndTimes)
-
1
module Cucumber
-
1
module Rails
-
1
module Database
-
-
1
CUSTOM_STRATEGY_INTERFACE = %w{ before_js before_non_js }
-
-
1
class InvalidStrategy < ArgumentError;end
-
-
1
class << self
-
-
1
def javascript_strategy=(args)
-
2
strategy, *strategy_opts = args
-
2
strategy_type =
-
case strategy
-
when Symbol
-
2
map[strategy] || raise(InvalidStrategy, "The strategy '#{strategy}' is not understood. Please use one of #{map.keys.join(',')}")
-
when Class
-
strategy
-
end
-
-
2
@strategy = strategy_type.new(*strategy_opts)
-
-
2
validate_interface!
-
end
-
-
1
def before_js
-
@strategy.before_js
-
end
-
-
1
def before_non_js
-
@strategy.before_non_js
-
end
-
-
1
private
-
-
1
def map
-
{
-
:truncation => TruncationStrategy,
-
:shared_connection => SharedConnectionStrategy,
-
:transaction => SharedConnectionStrategy,
-
:deletion => DeletionStrategy
-
2
}
-
end
-
-
1
def validate_interface!
-
6
unless CUSTOM_STRATEGY_INTERFACE.all? {|m| @strategy.respond_to?(m) }
-
raise(ArgumentError, "Strategy must respond to all of: #{CUSTOM_STRATEGY_INTERFACE.map{|method| "##{method}" } * ' '} !")
-
end
-
end
-
-
end
-
-
1
class SharedConnectionStrategy
-
1
def before_js
-
# Forces all threads to share a connection on a per-model basis,
-
# as connections may vary per model as per establish_connection. This works
-
# on Capybara because it starts the web server in a thread.
-
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
-
ActiveRecord::Base.descendants.each do |model|
-
model.shared_connection = model.connection
-
end
-
end
-
-
1
def before_non_js
-
# Do not use a shared connection unless we're in a @javascript scenario
-
ActiveRecord::Base.shared_connection = nil
-
ActiveRecord::Base.descendants.each do |model|
-
model.shared_connection = nil
-
end
-
end
-
end
-
-
1
class Strategy
-
1
def initialize(options={})
-
2
@options=options
-
end
-
-
1
def before_js(strategy)
-
@original_strategy = DatabaseCleaner.connections.first.strategy # that feels like a nasty hack
-
DatabaseCleaner.strategy = strategy, @options
-
end
-
-
1
def before_non_js
-
return unless @original_strategy
-
DatabaseCleaner.strategy = @original_strategy
-
@original_strategy = nil
-
end
-
end
-
-
1
class TruncationStrategy < Strategy
-
1
def before_js
-
super :truncation
-
end
-
end
-
-
1
class DeletionStrategy < Strategy
-
1
def before_js
-
super :deletion
-
end
-
end
-
-
1
Database.javascript_strategy = :truncation
-
end
-
end
-
end
-
1
require 'cucumber/rails/hooks/active_record'
-
1
require 'cucumber/rails/hooks/database_cleaner'
-
1
require 'cucumber/rails/hooks/allow_rescue'
-
1
require 'cucumber/rails/hooks/mail'
-
1
if defined?(ActiveRecord::Base)
-
1
class ActiveRecord::Base
-
1
class_attribute :shared_connection
-
-
1
def self.connection
-
self.shared_connection || retrieve_connection
-
end
-
end
-
-
1
Before('@javascript') do
-
Cucumber::Rails::Database.before_js
-
end
-
-
1
Before('~@javascript') do
-
Cucumber::Rails::Database.before_non_js
-
end
-
end
-
1
Before('@allow-rescue') do
-
@__orig_allow_rescue = ActionController::Base.allow_rescue
-
ActionController::Base.allow_rescue = true
-
end
-
-
1
After('@allow-rescue') do
-
ActionController::Base.allow_rescue = @__orig_allow_rescue
-
end
-
1
begin
-
1
require 'database_cleaner'
-
-
1
Before('~@no-database-cleaner') do
-
DatabaseCleaner.start
-
end
-
-
1
After('~@no-database-cleaner') do
-
DatabaseCleaner.clean
-
end
-
-
rescue LoadError => ignore_if_database_cleaner_not_present
-
end
-
1
if defined?(ActionMailer::Base)
-
1
Before do
-
ActionMailer::Base.deliveries = []
-
end
-
end
-
1
begin
-
# Try to load it so we can assign @_result below if needed.
-
1
require 'test/unit/testresult'
-
rescue LoadError => ignore
-
end
-
-
1
module Cucumber #:nodoc:
-
1
module Rails #:nodoc:
-
1
class World < ActionController::IntegrationTest #:nodoc:
-
1
include Rack::Test::Methods
-
1
include ActiveSupport::Testing::SetupAndTeardown if ActiveSupport::Testing.const_defined?("SetupAndTeardown")
-
-
1
def initialize #:nodoc:
-
@_result = Test::Unit::TestResult.new if defined?(Test::Unit::TestResult)
-
end
-
-
1
if !defined?(ActiveRecord::Base)
-
def self.fixture_table_names; []; end # Workaround for projects that don't use ActiveRecord
-
end
-
end
-
end
-
end
-
-
1
World do
-
Cucumber::Rails::World.new
-
end
-
1
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))
-
1
require 'database_cleaner/configuration'
-
-
1
require 'database_cleaner/generic/base'
-
1
require 'active_record'
-
1
require 'erb'
-
-
1
module DatabaseCleaner
-
1
module ActiveRecord
-
-
1
def self.available_strategies
-
%w[truncation transaction deletion]
-
end
-
-
1
def self.config_file_location=(path)
-
@config_file_location = path
-
end
-
-
1
def self.config_file_location
-
@config_file_location ||= "#{DatabaseCleaner.app_root}/config/database.yml"
-
end
-
-
1
module Base
-
1
include ::DatabaseCleaner::Generic::Base
-
-
1
attr_accessor :connection_hash
-
-
1
def db=(desired_db)
-
2
@db = desired_db
-
2
load_config
-
end
-
-
1
def db
-
2
@db || super
-
end
-
-
1
def load_config
-
2
if self.db != :default && File.file?(ActiveRecord.config_file_location)
-
connection_details = YAML::load(ERB.new(IO.read(ActiveRecord.config_file_location)).result)
-
@connection_hash = connection_details[self.db.to_s]
-
end
-
end
-
-
1
def create_connection_klass
-
Class.new(::ActiveRecord::Base)
-
end
-
-
1
def connection_klass
-
return ::ActiveRecord::Base unless connection_hash
-
klass = create_connection_klass
-
klass.send :establish_connection, connection_hash
-
klass
-
end
-
end
-
end
-
end
-
1
require 'database_cleaner/active_record/base'
-
1
module DatabaseCleaner::ActiveRecord
-
1
class Transaction
-
1
include ::DatabaseCleaner::ActiveRecord::Base
-
-
1
def start
-
if connection_klass.connection.respond_to?(:increment_open_transactions)
-
connection_klass.connection.increment_open_transactions
-
else
-
connection_klass.__send__(:increment_open_transactions)
-
end
-
connection_klass.connection.begin_db_transaction
-
end
-
-
-
1
def clean
-
return unless connection_klass.connection.open_transactions > 0
-
-
connection_klass.connection.rollback_db_transaction
-
-
if connection_klass.connection.respond_to?(:decrement_open_transactions)
-
connection_klass.connection.decrement_open_transactions
-
else
-
connection_klass.__send__(:decrement_open_transactions)
-
end
-
end
-
end
-
end
-
1
require 'database_cleaner/null_strategy'
-
1
module DatabaseCleaner
-
1
class Base
-
-
1
def initialize(desired_orm = nil,opts = {})
-
1
if [:autodetect, nil, "autodetect"].include?(desired_orm)
-
1
autodetect
-
else
-
self.orm = desired_orm
-
end
-
1
self.db = opts[:connection] if opts.has_key? :connection
-
1
set_default_orm_strategy
-
end
-
-
1
def db=(desired_db)
-
self.strategy_db = desired_db
-
@db = desired_db
-
end
-
-
1
def strategy_db=(desired_db)
-
2
if strategy.respond_to? :db=
-
2
strategy.db = desired_db
-
elsif desired_db!= :default
-
raise ArgumentError, "You must provide a strategy object that supports non default databases when you specify a database"
-
end
-
end
-
-
1
def db
-
2
@db || :default
-
end
-
-
1
def create_strategy(*args)
-
2
strategy, *strategy_args = args
-
2
orm_strategy(strategy).new(*strategy_args)
-
end
-
-
1
def clean_with(*args)
-
strategy = create_strategy(*args)
-
strategy.clean
-
strategy
-
end
-
-
1
alias clean_with! clean_with
-
-
1
def strategy=(args)
-
2
strategy, *strategy_args = args
-
2
if strategy.is_a?(Symbol)
-
2
@strategy = create_strategy(*args)
-
elsif strategy_args.empty?
-
@strategy = strategy
-
else
-
raise ArgumentError, "You must provide a strategy object, or a symbol for a known strategy along with initialization params."
-
end
-
-
2
self.strategy_db = self.db
-
-
2
@strategy
-
end
-
-
1
def strategy
-
4
@strategy || NullStrategy
-
end
-
-
1
def orm=(desired_orm)
-
@orm = desired_orm.to_sym
-
end
-
-
1
def orm
-
5
@orm || autodetect
-
end
-
-
1
def start
-
strategy.start
-
end
-
-
1
def clean
-
strategy.clean
-
end
-
-
1
alias clean! clean
-
-
1
def auto_detected?
-
!!@autodetected
-
end
-
-
#TODO make strategies directly comparable
-
1
def ==(other)
-
self.orm == other.orm && self.db == other.db && self.strategy.class == other.strategy.class
-
end
-
-
1
private
-
-
1
def orm_module
-
2
::DatabaseCleaner.orm_module(orm)
-
end
-
-
1
def orm_strategy(strategy)
-
2
require "database_cleaner/#{orm.to_s}/#{strategy.to_s}"
-
2
orm_module.const_get(strategy.to_s.capitalize)
-
rescue LoadError => e
-
if orm_module.respond_to? :available_strategies
-
raise UnknownStrategySpecified, "The '#{strategy}' strategy does not exist for the #{orm} ORM! Available strategies: #{orm_module.available_strategies.join(', ')}"
-
else
-
raise UnknownStrategySpecified, "The '#{strategy}' strategy does not exist for the #{orm} ORM!"
-
end
-
end
-
-
1
def autodetect
-
@orm ||= begin
-
1
@autodetected = true
-
1
if defined? ::ActiveRecord
-
1
:active_record
-
elsif defined? ::DataMapper
-
:data_mapper
-
elsif defined? ::MongoMapper
-
:mongo_mapper
-
elsif defined? ::Mongoid
-
:mongoid
-
elsif defined? ::CouchPotato
-
:couch_potato
-
elsif defined? ::Sequel
-
:sequel
-
else
-
raise NoORMDetected, "No known ORM was detected! Is ActiveRecord, DataMapper, Sequel, MongoMapper, Mongoid, or CouchPotato loaded?"
-
end
-
1
end
-
end
-
-
1
def set_default_orm_strategy
-
1
case orm
-
when :active_record, :data_mapper, :sequel
-
1
self.strategy = :transaction
-
when :mongo_mapper, :mongoid, :couch_potato
-
self.strategy = :truncation
-
end
-
end
-
end
-
end
-
1
require 'database_cleaner/base'
-
-
1
module DatabaseCleaner
-
-
1
class NoORMDetected < StandardError; end
-
1
class UnknownStrategySpecified < ArgumentError; end
-
-
1
class << self
-
1
def [](orm,opts = {})
-
raise NoORMDetected unless orm
-
@connections ||= []
-
cleaner = DatabaseCleaner::Base.new(orm,opts)
-
connections.push cleaner
-
cleaner
-
end
-
-
1
def app_root=(desired_root)
-
@app_root = desired_root
-
end
-
-
1
def app_root
-
@app_root || Dir.pwd
-
end
-
-
1
def connections
-
2
@connections ||= [::DatabaseCleaner::Base.new]
-
end
-
-
1
def logger=(log_source)
-
@logger = log_source
-
end
-
-
1
def logger
-
return @logger if @logger
-
-
@logger = Logger.new(STDOUT)
-
@logger.level = Logger::ERROR
-
@logger
-
end
-
-
1
def strategy=(stratagem)
-
2
connections.each { |connect| connect.strategy = stratagem }
-
1
remove_duplicates
-
end
-
-
1
def orm=(orm)
-
connections.each { |connect| connect.orm = orm }
-
remove_duplicates
-
end
-
-
1
def start
-
connections.each { |connection| connection.start }
-
end
-
-
1
def clean
-
connections.each { |connection| connection.clean }
-
end
-
-
1
alias clean! clean
-
-
1
def clean_with(*args)
-
connections.each { |connection| connection.clean_with(*args) }
-
end
-
-
1
alias clean_with! clean_with
-
-
1
def remove_duplicates
-
1
temp = []
-
1
connections.each do |connect|
-
1
temp.push connect unless temp.include? connect
-
end
-
1
@connections = temp
-
end
-
-
1
def orm_module(symbol)
-
2
case symbol
-
when :active_record
-
2
DatabaseCleaner::ActiveRecord
-
when :data_mapper
-
DatabaseCleaner::DataMapper
-
when :mongo
-
DatabaseCleaner::Mongo
-
when :mongoid
-
DatabaseCleaner::Mongoid
-
when :mongo_mapper
-
DatabaseCleaner::MongoMapper
-
when :couch_potato
-
DatabaseCleaner::CouchPotato
-
when :sequel
-
DatabaseCleaner::Sequel
-
end
-
end
-
end
-
end
-
1
module ::DatabaseCleaner
-
1
module Generic
-
1
module Base
-
-
1
def self.included(base)
-
1
base.extend(ClassMethods)
-
end
-
-
1
def db
-
:default
-
end
-
-
1
module ClassMethods
-
1
def available_strategies
-
%W[]
-
end
-
end
-
end
-
end
-
end
-
1
module DatabaseCleaner
-
1
class NullStrategy
-
1
def self.start
-
# no-op
-
end
-
-
1
def self.db=(connection)
-
# no-op
-
end
-
-
1
def self.clean
-
# no-op
-
end
-
end
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
##
-
## an implementation of eRuby
-
##
-
## ex.
-
## input = <<'END'
-
## <ul>
-
## <% for item in @list %>
-
## <li><%= item %>
-
## <%== item %></li>
-
## <% end %>
-
## </ul>
-
## END
-
## list = ['<aaa>', 'b&b', '"ccc"']
-
## eruby = Erubis::Eruby.new(input)
-
## puts "--- code ---"
-
## puts eruby.src
-
## puts "--- result ---"
-
## context = Erubis::Context.new() # or new(:list=>list)
-
## context[:list] = list
-
## puts eruby.evaluate(context)
-
##
-
## result:
-
## --- source ---
-
## _buf = ''; _buf << '<ul>
-
## '; for item in @list
-
## _buf << ' <li>'; _buf << ( item ).to_s; _buf << '
-
## '; _buf << ' '; _buf << Erubis::XmlHelper.escape_xml( item ); _buf << '</li>
-
## '; end
-
## _buf << '</ul>
-
## ';
-
## _buf.to_s
-
## --- result ---
-
## <ul>
-
## <li><aaa>
-
## <aaa></li>
-
## <li>b&b
-
## b&b</li>
-
## <li>"ccc"
-
## "ccc"</li>
-
## </ul>
-
##
-
-
-
1
module Erubis
-
1
VERSION = ('$Release: 2.7.0 $' =~ /([.\d]+)/) && $1
-
end
-
-
1
require 'erubis/engine'
-
#require 'erubis/generator'
-
#require 'erubis/converter'
-
#require 'erubis/evaluator'
-
#require 'erubis/error'
-
#require 'erubis/context'
-
#requier 'erubis/util'
-
1
require 'erubis/helper'
-
1
require 'erubis/enhancer'
-
#require 'erubis/tiny'
-
1
require 'erubis/engine/eruby'
-
#require 'erubis/engine/enhanced' # enhanced eruby engines
-
#require 'erubis/engine/optimized' # generates optimized ruby code
-
#require 'erubis/engine/ephp'
-
#require 'erubis/engine/ec'
-
#require 'erubis/engine/ejava'
-
#require 'erubis/engine/escheme'
-
#require 'erubis/engine/eperl'
-
#require 'erubis/engine/ejavascript'
-
-
1
require 'erubis/local-setting'
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
-
1
module Erubis
-
-
-
##
-
## context object for Engine#evaluate
-
##
-
## ex.
-
## template = <<'END'
-
## Hello <%= @user %>!
-
## <% for item in @list %>
-
## - <%= item %>
-
## <% end %>
-
## END
-
##
-
## context = Erubis::Context.new(:user=>'World', :list=>['a','b','c'])
-
## # or
-
## # context = Erubis::Context.new
-
## # context[:user] = 'World'
-
## # context[:list] = ['a', 'b', 'c']
-
##
-
## eruby = Erubis::Eruby.new(template)
-
## print eruby.evaluate(context)
-
##
-
1
class Context
-
1
include Enumerable
-
-
1
def initialize(hash=nil)
-
hash.each do |name, value|
-
self[name] = value
-
end if hash
-
end
-
-
1
def [](key)
-
return instance_variable_get("@#{key}")
-
end
-
-
1
def []=(key, value)
-
return instance_variable_set("@#{key}", value)
-
end
-
-
1
def keys
-
return instance_variables.collect { |name| name[1..-1] }
-
end
-
-
1
def each
-
instance_variables.each do |name|
-
key = name[1..-1]
-
value = instance_variable_get(name)
-
yield(key, value)
-
end
-
end
-
-
1
def to_hash
-
hash = {}
-
self.keys.each { |key| hash[key] = self[key] }
-
return hash
-
end
-
-
1
def update(context_or_hash)
-
arg = context_or_hash
-
if arg.is_a?(Hash)
-
arg.each do |key, val|
-
self[key] = val
-
end
-
else
-
arg.instance_variables.each do |varname|
-
key = varname[1..-1]
-
val = arg.instance_variable_get(varname)
-
self[key] = val
-
end
-
end
-
end
-
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
1
require 'erubis/util'
-
-
1
module Erubis
-
-
-
##
-
## convert
-
##
-
1
module Converter
-
-
1
attr_accessor :preamble, :postamble, :escape
-
-
1
def self.supported_properties # :nodoc:
-
return [
-
[:preamble, nil, "preamble (no preamble when false)"],
-
[:postamble, nil, "postamble (no postamble when false)"],
-
[:escape, nil, "escape expression or not in default"],
-
]
-
end
-
-
1
def init_converter(properties={})
-
@preamble = properties[:preamble]
-
@postamble = properties[:postamble]
-
@escape = properties[:escape]
-
end
-
-
## convert input string into target language
-
1
def convert(input)
-
codebuf = "" # or []
-
@preamble.nil? ? add_preamble(codebuf) : (@preamble && (codebuf << @preamble))
-
convert_input(codebuf, input)
-
@postamble.nil? ? add_postamble(codebuf) : (@postamble && (codebuf << @postamble))
-
@_proc = nil # clear cached proc object
-
return codebuf # or codebuf.join()
-
end
-
-
1
protected
-
-
##
-
## detect spaces at beginning of line
-
##
-
1
def detect_spaces_at_bol(text, is_bol)
-
lspace = nil
-
if text.empty?
-
lspace = "" if is_bol
-
elsif text[-1] == ?\n
-
lspace = ""
-
else
-
rindex = text.rindex(?\n)
-
if rindex
-
s = text[rindex+1..-1]
-
if s =~ /\A[ \t]*\z/
-
lspace = s
-
#text = text[0..rindex]
-
text[rindex+1..-1] = ''
-
end
-
else
-
if is_bol && text =~ /\A[ \t]*\z/
-
#lspace = text
-
#text = nil
-
lspace = text.dup
-
text[0..-1] = ''
-
end
-
end
-
end
-
return lspace
-
end
-
-
##
-
## (abstract) convert input to code
-
##
-
1
def convert_input(codebuf, input)
-
not_implemented
-
end
-
-
end
-
-
-
1
module Basic
-
end
-
-
-
##
-
## basic converter which supports '<% ... %>' notation.
-
##
-
1
module Basic::Converter
-
1
include Erubis::Converter
-
-
1
def self.supported_properties # :nodoc:
-
return [
-
[:pattern, '<% %>', "embed pattern"],
-
[:trim, true, "trim spaces around <% ... %>"],
-
]
-
end
-
-
1
attr_accessor :pattern, :trim
-
-
1
def init_converter(properties={})
-
super(properties)
-
@pattern = properties[:pattern]
-
@trim = properties[:trim] != false
-
end
-
-
1
protected
-
-
## return regexp of pattern to parse eRuby script
-
1
def pattern_regexp(pattern)
-
1
@prefix, @postfix = pattern.split() # '<% %>' => '<%', '%>'
-
#return /(.*?)(^[ \t]*)?#{@prefix}(=+|\#)?(.*?)-?#{@postfix}([ \t]*\r?\n)?/m
-
#return /(^[ \t]*)?#{@prefix}(=+|\#)?(.*?)-?#{@postfix}([ \t]*\r?\n)?/m
-
1
return /#{@prefix}(=+|-|\#|%)?(.*?)([-=])?#{@postfix}([ \t]*\r?\n)?/m
-
end
-
1
module_function :pattern_regexp
-
-
#DEFAULT_REGEXP = /(.*?)(^[ \t]*)?<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
-
#DEFAULT_REGEXP = /(^[ \t]*)?<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
-
#DEFAULT_REGEXP = /<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
-
1
DEFAULT_REGEXP = pattern_regexp('<% %>')
-
-
1
public
-
-
1
def convert_input(src, input)
-
pat = @pattern
-
regexp = pat.nil? || pat == '<% %>' ? DEFAULT_REGEXP : pattern_regexp(pat)
-
pos = 0
-
is_bol = true # is beginning of line
-
input.scan(regexp) do |indicator, code, tailch, rspace|
-
match = Regexp.last_match()
-
len = match.begin(0) - pos
-
text = input[pos, len]
-
pos = match.end(0)
-
ch = indicator ? indicator[0] : nil
-
lspace = ch == ?= ? nil : detect_spaces_at_bol(text, is_bol)
-
is_bol = rspace ? true : false
-
add_text(src, text) if text && !text.empty?
-
## * when '<%= %>', do nothing
-
## * when '<% %>' or '<%# %>', delete spaces iff only spaces are around '<% %>'
-
if ch == ?= # <%= %>
-
rspace = nil if tailch && !tailch.empty?
-
add_text(src, lspace) if lspace
-
add_expr(src, code, indicator)
-
add_text(src, rspace) if rspace
-
elsif ch == ?\# # <%# %>
-
n = code.count("\n") + (rspace ? 1 : 0)
-
if @trim && lspace && rspace
-
add_stmt(src, "\n" * n)
-
else
-
add_text(src, lspace) if lspace
-
add_stmt(src, "\n" * n)
-
add_text(src, rspace) if rspace
-
end
-
elsif ch == ?% # <%% %>
-
s = "#{lspace}#{@prefix||='<%'}#{code}#{tailch}#{@postfix||='%>'}#{rspace}"
-
add_text(src, s)
-
else # <% %>
-
if @trim && lspace && rspace
-
add_stmt(src, "#{lspace}#{code}#{rspace}")
-
else
-
add_text(src, lspace) if lspace
-
add_stmt(src, code)
-
add_text(src, rspace) if rspace
-
end
-
end
-
end
-
#rest = $' || input # ruby1.8
-
rest = pos == 0 ? input : input[pos..-1] # ruby1.9
-
add_text(src, rest)
-
end
-
-
## add expression code to src
-
1
def add_expr(src, code, indicator)
-
case indicator
-
when '='
-
@escape ? add_expr_escaped(src, code) : add_expr_literal(src, code)
-
when '=='
-
@escape ? add_expr_literal(src, code) : add_expr_escaped(src, code)
-
when '==='
-
add_expr_debug(src, code)
-
end
-
end
-
-
end
-
-
-
1
module PI
-
end
-
-
##
-
## Processing Instructions (PI) converter for XML.
-
## this class converts '<?rb ... ?>' and '${...}' notation.
-
##
-
1
module PI::Converter
-
1
include Erubis::Converter
-
-
1
def self.desc # :nodoc:
-
"use processing instructions (PI) instead of '<% %>'"
-
end
-
-
1
def self.supported_properties # :nodoc:
-
return [
-
[:trim, true, "trim spaces around <% ... %>"],
-
[:pi, 'rb', "PI (Processing Instrunctions) name"],
-
[:embchar, '@', "char for embedded expression pattern('@{...}@')"],
-
[:pattern, '<% %>', "embed pattern"],
-
]
-
end
-
-
1
attr_accessor :pi, :prefix
-
-
1
def init_converter(properties={})
-
super(properties)
-
@trim = properties.fetch(:trim, true)
-
@pi = properties[:pi] if properties[:pi]
-
@embchar = properties[:embchar] || '@'
-
@pattern = properties[:pattern]
-
@pattern = '<% %>' if @pattern.nil? #|| @pattern == true
-
end
-
-
1
def convert(input)
-
code = super(input)
-
return @header || @footer ? "#{@header}#{code}#{@footer}" : code
-
end
-
-
1
protected
-
-
1
def convert_input(codebuf, input)
-
unless @regexp
-
@pi ||= 'e'
-
ch = Regexp.escape(@embchar)
-
if @pattern
-
left, right = @pattern.split(' ')
-
@regexp = /<\?#{@pi}(?:-(\w+))?(\s.*?)\?>([ \t]*\r?\n)?|#{ch}(!*)?\{(.*?)\}#{ch}|#{left}(=+)(.*?)#{right}/m
-
else
-
@regexp = /<\?#{@pi}(?:-(\w+))?(\s.*?)\?>([ \t]*\r?\n)?|#{ch}(!*)?\{(.*?)\}#{ch}/m
-
end
-
end
-
#
-
is_bol = true
-
pos = 0
-
input.scan(@regexp) do |pi_arg, stmt, rspace,
-
indicator1, expr1, indicator2, expr2|
-
match = Regexp.last_match
-
len = match.begin(0) - pos
-
text = input[pos, len]
-
pos = match.end(0)
-
lspace = stmt ? detect_spaces_at_bol(text, is_bol) : nil
-
is_bol = stmt && rspace ? true : false
-
add_text(codebuf, text) # unless text.empty?
-
#
-
if stmt
-
if @trim && lspace && rspace
-
add_pi_stmt(codebuf, "#{lspace}#{stmt}#{rspace}", pi_arg)
-
else
-
add_text(codebuf, lspace) if lspace
-
add_pi_stmt(codebuf, stmt, pi_arg)
-
add_text(codebuf, rspace) if rspace
-
end
-
else
-
add_pi_expr(codebuf, expr1 || expr2, indicator1 || indicator2)
-
end
-
end
-
#rest = $' || input # ruby1.8
-
rest = pos == 0 ? input : input[pos..-1] # ruby1.9
-
add_text(codebuf, rest)
-
end
-
-
#--
-
#def convert_input(codebuf, input)
-
# parse_stmts(codebuf, input)
-
# #parse_stmts2(codebuf, input)
-
#end
-
#
-
#def parse_stmts(codebuf, input)
-
# #regexp = pattern_regexp(@pattern)
-
# @pi ||= 'e'
-
# @stmt_pattern ||= /<\?#{@pi}(?:-(\w+))?(\s.*?)\?>([ \t]*\r?\n)?/m
-
# is_bol = true
-
# pos = 0
-
# input.scan(@stmt_pattern) do |pi_arg, code, rspace|
-
# match = Regexp.last_match
-
# len = match.begin(0) - pos
-
# text = input[pos, len]
-
# pos = match.end(0)
-
# lspace = detect_spaces_at_bol(text, is_bol)
-
# is_bol = rspace ? true : false
-
# parse_exprs(codebuf, text) # unless text.empty?
-
# if @trim && lspace && rspace
-
# add_pi_stmt(codebuf, "#{lspace}#{code}#{rspace}", pi_arg)
-
# else
-
# add_text(codebuf, lspace)
-
# add_pi_stmt(codebuf, code, pi_arg)
-
# add_text(codebuf, rspace)
-
# end
-
# end
-
# rest = $' || input
-
# parse_exprs(codebuf, rest)
-
#end
-
#
-
#def parse_exprs(codebuf, input)
-
# unless @expr_pattern
-
# ch = Regexp.escape(@embchar)
-
# if @pattern
-
# left, right = @pattern.split(' ')
-
# @expr_pattern = /#{ch}(!*)?\{(.*?)\}#{ch}|#{left}(=+)(.*?)#{right}/
-
# else
-
# @expr_pattern = /#{ch}(!*)?\{(.*?)\}#{ch}/
-
# end
-
# end
-
# pos = 0
-
# input.scan(@expr_pattern) do |indicator1, code1, indicator2, code2|
-
# indicator = indicator1 || indicator2
-
# code = code1 || code2
-
# match = Regexp.last_match
-
# len = match.begin(0) - pos
-
# text = input[pos, len]
-
# pos = match.end(0)
-
# add_text(codebuf, text) # unless text.empty?
-
# add_pi_expr(codebuf, code, indicator)
-
# end
-
# rest = $' || input
-
# add_text(codebuf, rest)
-
#end
-
#++
-
-
1
def add_pi_stmt(codebuf, code, pi_arg) # :nodoc:
-
case pi_arg
-
when nil ; add_stmt(codebuf, code)
-
when 'header' ; @header = code
-
when 'footer' ; @footer = code
-
when 'comment'; add_stmt(codebuf, "\n" * code.count("\n"))
-
when 'value' ; add_expr_literal(codebuf, code)
-
else ; add_stmt(codebuf, code)
-
end
-
end
-
-
1
def add_pi_expr(codebuf, code, indicator) # :nodoc:
-
case indicator
-
when nil, '', '==' # @{...}@ or <%== ... %>
-
@escape == false ? add_expr_literal(codebuf, code) : add_expr_escaped(codebuf, code)
-
when '!', '=' # @!{...}@ or <%= ... %>
-
@escape == false ? add_expr_escaped(codebuf, code) : add_expr_literal(codebuf, code)
-
when '!!', '===' # @!!{...}@ or <%=== ... %>
-
add_expr_debug(codebuf, code)
-
else
-
# ignore
-
end
-
end
-
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
-
1
require 'erubis/generator'
-
1
require 'erubis/converter'
-
1
require 'erubis/evaluator'
-
1
require 'erubis/context'
-
-
-
1
module Erubis
-
-
-
##
-
## (abstract) abstract engine class.
-
## subclass must include evaluator and converter module.
-
##
-
1
class Engine
-
#include Evaluator
-
#include Converter
-
#include Generator
-
-
1
def initialize(input=nil, properties={})
-
#@input = input
-
init_generator(properties)
-
init_converter(properties)
-
init_evaluator(properties)
-
@src = convert(input) if input
-
end
-
-
-
##
-
## convert input string and set it to @src
-
##
-
1
def convert!(input)
-
@src = convert(input)
-
end
-
-
-
##
-
## load file, write cache file, and return engine object.
-
## this method create code cache file automatically.
-
## cachefile name can be specified with properties[:cachename],
-
## or filname + 'cache' is used as default.
-
##
-
1
def self.load_file(filename, properties={})
-
cachename = properties[:cachename] || (filename + '.cache')
-
properties[:filename] = filename
-
timestamp = File.mtime(filename)
-
if test(?f, cachename) && timestamp == File.mtime(cachename)
-
engine = self.new(nil, properties)
-
engine.src = File.read(cachename)
-
else
-
input = File.open(filename, 'rb') {|f| f.read }
-
engine = self.new(input, properties)
-
tmpname = cachename + rand().to_s[1,8]
-
File.open(tmpname, 'wb') {|f| f.write(engine.src) }
-
File.rename(tmpname, cachename)
-
File.utime(timestamp, timestamp, cachename)
-
end
-
engine.src.untaint # ok?
-
return engine
-
end
-
-
-
##
-
## helper method to convert and evaluate input text with context object.
-
## context may be Binding, Hash, or Object.
-
##
-
1
def process(input, context=nil, filename=nil)
-
code = convert(input)
-
filename ||= '(erubis)'
-
if context.is_a?(Binding)
-
return eval(code, context, filename)
-
else
-
context = Context.new(context) if context.is_a?(Hash)
-
return context.instance_eval(code, filename)
-
end
-
end
-
-
-
##
-
## helper method evaluate Proc object with contect object.
-
## context may be Binding, Hash, or Object.
-
##
-
1
def process_proc(proc_obj, context=nil, filename=nil)
-
if context.is_a?(Binding)
-
filename ||= '(erubis)'
-
return eval(proc_obj, context, filename)
-
else
-
context = Context.new(context) if context.is_a?(Hash)
-
return context.instance_eval(&proc_obj)
-
end
-
end
-
-
-
end # end of class Engine
-
-
-
##
-
## (abstract) base engine class for Eruby, Eperl, Ejava, and so on.
-
## subclass must include generator.
-
##
-
1
class Basic::Engine < Engine
-
1
include Evaluator
-
1
include Basic::Converter
-
1
include Generator
-
end
-
-
-
1
class PI::Engine < Engine
-
1
include Evaluator
-
1
include PI::Converter
-
1
include Generator
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
1
require 'erubis/engine'
-
1
require 'erubis/enhancer'
-
-
-
1
module Erubis
-
-
-
##
-
## code generator for Ruby
-
##
-
1
module RubyGenerator
-
1
include Generator
-
#include ArrayBufferEnhancer
-
1
include StringBufferEnhancer
-
-
1
def init_generator(properties={})
-
super
-
@escapefunc ||= "Erubis::XmlHelper.escape_xml"
-
@bufvar = properties[:bufvar] || "_buf"
-
end
-
-
1
def self.supported_properties() # :nodoc:
-
return []
-
end
-
-
1
def escape_text(text)
-
text.gsub(/['\\]/, '\\\\\&') # "'" => "\\'", '\\' => '\\\\'
-
end
-
-
1
def escaped_expr(code)
-
return "#{@escapefunc}(#{code})"
-
end
-
-
#--
-
#def add_preamble(src)
-
# src << "#{@bufvar} = [];"
-
#end
-
#++
-
-
1
def add_text(src, text)
-
src << " #{@bufvar} << '" << escape_text(text) << "';" unless text.empty?
-
end
-
-
1
def add_stmt(src, code)
-
#src << code << ';'
-
src << code
-
src << ';' unless code[-1] == ?\n
-
end
-
-
1
def add_expr_literal(src, code)
-
src << " #{@bufvar} << (" << code << ').to_s;'
-
end
-
-
1
def add_expr_escaped(src, code)
-
src << " #{@bufvar} << " << escaped_expr(code) << ';'
-
end
-
-
1
def add_expr_debug(src, code)
-
code.strip!
-
s = (code.dump =~ /\A"(.*)"\z/) && $1
-
src << ' $stderr.puts("*** debug: ' << s << '=#{(' << code << ').inspect}");'
-
end
-
-
#--
-
#def add_postamble(src)
-
# src << "\n#{@bufvar}.join\n"
-
#end
-
#++
-
-
end
-
-
-
##
-
## engine for Ruby
-
##
-
1
class Eruby < Basic::Engine
-
1
include RubyEvaluator
-
1
include RubyGenerator
-
end
-
-
-
##
-
## fast engine for Ruby
-
##
-
1
class FastEruby < Eruby
-
1
include InterpolationEnhancer
-
end
-
-
-
##
-
## swtich '<%= %>' to escaped and '<%== %>' to not escaped
-
##
-
1
class EscapedEruby < Eruby
-
1
include EscapeEnhancer
-
end
-
-
-
##
-
## sanitize expression (<%= ... %>) by default
-
##
-
## this is equivalent to EscapedEruby and is prepared only for compatibility.
-
##
-
1
class XmlEruby < Eruby
-
1
include EscapeEnhancer
-
end
-
-
-
1
class PI::Eruby < PI::Engine
-
1
include RubyEvaluator
-
1
include RubyGenerator
-
-
1
def init_converter(properties={})
-
@pi = 'rb'
-
super(properties)
-
end
-
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
-
1
module Erubis
-
-
-
##
-
## switch '<%= ... %>' to escaped and '<%== ... %>' to unescaped
-
##
-
## ex.
-
## class XmlEruby < Eruby
-
## include EscapeEnhancer
-
## end
-
##
-
## this is language-indenedent.
-
##
-
1
module EscapeEnhancer
-
-
1
def self.desc # :nodoc:
-
"switch '<%= %>' to escaped and '<%== %>' to unescaped"
-
end
-
-
#--
-
#def self.included(klass)
-
# klass.class_eval <<-END
-
# alias _add_expr_literal add_expr_literal
-
# alias _add_expr_escaped add_expr_escaped
-
# alias add_expr_literal _add_expr_escaped
-
# alias add_expr_escaped _add_expr_literal
-
# END
-
#end
-
#++
-
-
1
def add_expr(src, code, indicator)
-
case indicator
-
when '='
-
@escape ? add_expr_literal(src, code) : add_expr_escaped(src, code)
-
when '=='
-
@escape ? add_expr_escaped(src, code) : add_expr_literal(src, code)
-
when '==='
-
add_expr_debug(src, code)
-
end
-
end
-
-
end
-
-
-
#--
-
## (obsolete)
-
#module FastEnhancer
-
#end
-
#++
-
-
-
##
-
## use $stdout instead of string
-
##
-
## this is only for Eruby.
-
##
-
1
module StdoutEnhancer
-
-
1
def self.desc # :nodoc:
-
"use $stdout instead of array buffer or string buffer"
-
end
-
-
1
def add_preamble(src)
-
src << "#{@bufvar} = $stdout;"
-
end
-
-
1
def add_postamble(src)
-
src << "\n''\n"
-
end
-
-
end
-
-
-
##
-
## use print statement instead of '_buf << ...'
-
##
-
## this is only for Eruby.
-
##
-
1
module PrintOutEnhancer
-
-
1
def self.desc # :nodoc:
-
"use print statement instead of '_buf << ...'"
-
end
-
-
1
def add_preamble(src)
-
end
-
-
1
def add_text(src, text)
-
src << " print '#{escape_text(text)}';" unless text.empty?
-
end
-
-
1
def add_expr_literal(src, code)
-
src << " print((#{code}).to_s);"
-
end
-
-
1
def add_expr_escaped(src, code)
-
src << " print #{escaped_expr(code)};"
-
end
-
-
1
def add_postamble(src)
-
src << "\n" unless src[-1] == ?\n
-
end
-
-
end
-
-
-
##
-
## enable print function
-
##
-
## Notice: use Eruby#evaluate() and don't use Eruby#result()
-
## to be enable print function.
-
##
-
## this is only for Eruby.
-
##
-
1
module PrintEnabledEnhancer
-
-
1
def self.desc # :nodoc:
-
"enable to use print function in '<% %>'"
-
end
-
-
1
def add_preamble(src)
-
src << "@_buf = "
-
super
-
end
-
-
1
def print(*args)
-
args.each do |arg|
-
@_buf << arg.to_s
-
end
-
end
-
-
1
def evaluate(context=nil)
-
_src = @src
-
if context.is_a?(Hash)
-
context.each do |key, val| instance_variable_set("@#{key}", val) end
-
elsif context
-
context.instance_variables.each do |name|
-
instance_variable_set(name, context.instance_variable_get(name))
-
end
-
end
-
return instance_eval(_src, (@filename || '(erubis)'))
-
end
-
-
end
-
-
-
##
-
## return array instead of string
-
##
-
## this is only for Eruby.
-
##
-
1
module ArrayEnhancer
-
-
1
def self.desc # :nodoc:
-
"return array instead of string"
-
end
-
-
1
def add_preamble(src)
-
src << "#{@bufvar} = [];"
-
end
-
-
1
def add_postamble(src)
-
src << "\n" unless src[-1] == ?\n
-
src << "#{@bufvar}\n"
-
end
-
-
end
-
-
-
##
-
## use an Array object as buffer (included in Eruby by default)
-
##
-
## this is only for Eruby.
-
##
-
1
module ArrayBufferEnhancer
-
-
1
def self.desc # :nodoc:
-
"use an Array object for buffering (included in Eruby class)"
-
end
-
-
1
def add_preamble(src)
-
src << "_buf = [];"
-
end
-
-
1
def add_postamble(src)
-
src << "\n" unless src[-1] == ?\n
-
src << "_buf.join\n"
-
end
-
-
end
-
-
-
##
-
## use String class for buffering
-
##
-
## this is only for Eruby.
-
##
-
1
module StringBufferEnhancer
-
-
1
def self.desc # :nodoc:
-
"use a String object for buffering"
-
end
-
-
1
def add_preamble(src)
-
src << "#{@bufvar} = '';"
-
end
-
-
1
def add_postamble(src)
-
src << "\n" unless src[-1] == ?\n
-
src << "#{@bufvar}.to_s\n"
-
end
-
-
end
-
-
-
##
-
## use StringIO class for buffering
-
##
-
## this is only for Eruby.
-
##
-
1
module StringIOEnhancer # :nodoc:
-
-
1
def self.desc # :nodoc:
-
"use a StringIO object for buffering"
-
end
-
-
1
def add_preamble(src)
-
src << "#{@bufvar} = StringIO.new;"
-
end
-
-
1
def add_postamble(src)
-
src << "\n" unless src[-1] == ?\n
-
src << "#{@bufvar}.string\n"
-
end
-
-
end
-
-
-
##
-
## set buffer variable name to '_erbout' as well as '_buf'
-
##
-
## this is only for Eruby.
-
##
-
1
module ErboutEnhancer
-
-
1
def self.desc # :nodoc:
-
"set '_erbout = _buf = \"\";' to be compatible with ERB."
-
end
-
-
1
def add_preamble(src)
-
src << "_erbout = #{@bufvar} = '';"
-
end
-
-
1
def add_postamble(src)
-
src << "\n" unless src[-1] == ?\n
-
src << "#{@bufvar}.to_s\n"
-
end
-
-
end
-
-
-
##
-
## remove text and leave code, especially useful when debugging.
-
##
-
## ex.
-
## $ erubis -s -E NoText file.eruby | more
-
##
-
## this is language independent.
-
##
-
1
module NoTextEnhancer
-
-
1
def self.desc # :nodoc:
-
"remove text and leave code (useful when debugging)"
-
end
-
-
1
def add_text(src, text)
-
src << ("\n" * text.count("\n"))
-
if text[-1] != ?\n
-
text =~ /^(.*?)\z/
-
src << (' ' * $1.length)
-
end
-
end
-
-
end
-
-
-
##
-
## remove code and leave text, especially useful when validating HTML tags.
-
##
-
## ex.
-
## $ erubis -s -E NoCode file.eruby | tidy -errors
-
##
-
## this is language independent.
-
##
-
1
module NoCodeEnhancer
-
-
1
def self.desc # :nodoc:
-
"remove code and leave text (useful when validating HTML)"
-
end
-
-
1
def add_preamble(src)
-
end
-
-
1
def add_postamble(src)
-
end
-
-
1
def add_text(src, text)
-
src << text
-
end
-
-
1
def add_expr(src, code, indicator)
-
src << "\n" * code.count("\n")
-
end
-
-
1
def add_stmt(src, code)
-
src << "\n" * code.count("\n")
-
end
-
-
end
-
-
-
##
-
## get convert faster, but spaces around '<%...%>' are not trimmed.
-
##
-
## this is language-independent.
-
##
-
1
module SimplifyEnhancer
-
-
1
def self.desc # :nodoc:
-
"get convert faster but leave spaces around '<% %>'"
-
end
-
-
#DEFAULT_REGEXP = /(^[ \t]*)?<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
-
1
SIMPLE_REGEXP = /<%(=+|\#)?(.*?)-?%>/m
-
-
1
def convert(input)
-
src = ""
-
add_preamble(src)
-
#regexp = pattern_regexp(@pattern)
-
pos = 0
-
input.scan(SIMPLE_REGEXP) do |indicator, code|
-
match = Regexp.last_match
-
index = match.begin(0)
-
text = input[pos, index - pos]
-
pos = match.end(0)
-
add_text(src, text)
-
if !indicator # <% %>
-
add_stmt(src, code)
-
elsif indicator[0] == ?\# # <%# %>
-
n = code.count("\n")
-
add_stmt(src, "\n" * n)
-
else # <%= %>
-
add_expr(src, code, indicator)
-
end
-
end
-
#rest = $' || input # ruby1.8
-
rest = pos == 0 ? input : input[pos..-1] # ruby1.9
-
add_text(src, rest)
-
add_postamble(src)
-
return src
-
end
-
-
end
-
-
-
##
-
## enable to use other embedded expression pattern (default is '\[= =\]').
-
##
-
## notice! this is an experimental. spec may change in the future.
-
##
-
## ex.
-
## input = <<END
-
## <% for item in list %>
-
## <%= item %> : <%== item %>
-
## [= item =] : [== item =]
-
## <% end %>
-
## END
-
##
-
## class BiPatternEruby
-
## include BiPatternEnhancer
-
## end
-
## eruby = BiPatternEruby.new(input, :bipattern=>'\[= =\]')
-
## list = ['<a>', 'b&b', '"c"']
-
## print eruby.result(binding())
-
##
-
## ## output
-
## <a> : <a>
-
## <a> : <a>
-
## b&b : b&b
-
## b&b : b&b
-
## "c" : "c"
-
## "c" : "c"
-
##
-
## this is language independent.
-
##
-
1
module BiPatternEnhancer
-
-
1
def self.desc # :nodoc:
-
"another embedded expression pattern (default '\[= =\]')."
-
end
-
-
1
def initialize(input, properties={})
-
self.bipattern = properties[:bipattern] # or '\$\{ \}'
-
super
-
end
-
-
## when pat is nil then '\[= =\]' is used
-
1
def bipattern=(pat) # :nodoc:
-
@bipattern = pat || '\[= =\]'
-
pre, post = @bipattern.split()
-
@bipattern_regexp = /(.*?)#{pre}(=*)(.*?)#{post}/m
-
end
-
-
1
def add_text(src, text)
-
return unless text
-
m = nil
-
text.scan(@bipattern_regexp) do |txt, indicator, code|
-
m = Regexp.last_match
-
super(src, txt)
-
add_expr(src, code, '=' + indicator)
-
end
-
#rest = $' || text # ruby1.8
-
rest = m ? text[m.end(0)..-1] : text # ruby1.9
-
super(src, rest)
-
end
-
-
end
-
-
-
##
-
## regards lines starting with '^[ \t]*%' as program code
-
##
-
## in addition you can specify prefix character (default '%')
-
##
-
## this is language-independent.
-
##
-
1
module PrefixedLineEnhancer
-
-
1
def self.desc # :nodoc:
-
"regard lines matched to '^[ \t]*%' as program code"
-
end
-
-
1
def init_generator(properties={})
-
super
-
@prefixchar = properties[:prefixchar]
-
end
-
-
1
def add_text(src, text)
-
unless @prefixrexp
-
@prefixchar ||= '%'
-
@prefixrexp = Regexp.compile("^([ \\t]*)\\#{@prefixchar}(.*?\\r?\\n)")
-
end
-
pos = 0
-
text2 = ''
-
text.scan(@prefixrexp) do
-
space = $1
-
line = $2
-
space, line = '', $1 unless $2
-
match = Regexp.last_match
-
len = match.begin(0) - pos
-
str = text[pos, len]
-
pos = match.end(0)
-
if text2.empty?
-
text2 = str
-
else
-
text2 << str
-
end
-
if line[0, 1] == @prefixchar
-
text2 << space << line
-
else
-
super(src, text2)
-
text2 = ''
-
add_stmt(src, space + line)
-
end
-
end
-
#rest = pos == 0 ? text : $' # ruby1.8
-
rest = pos == 0 ? text : text[pos..-1] # ruby1.9
-
unless text2.empty?
-
text2 << rest if rest
-
rest = text2
-
end
-
super(src, rest)
-
end
-
-
end
-
-
-
##
-
## regards lines starting with '%' as program code
-
##
-
## this is for compatibility to eruby and ERB.
-
##
-
## this is language-independent.
-
##
-
1
module PercentLineEnhancer
-
1
include PrefixedLineEnhancer
-
-
1
def self.desc # :nodoc:
-
"regard lines starting with '%' as program code"
-
end
-
-
#--
-
#def init_generator(properties={})
-
# super
-
# @prefixchar = '%'
-
# @prefixrexp = /^\%(.*?\r?\n)/
-
#end
-
#++
-
-
1
def add_text(src, text)
-
unless @prefixrexp
-
@prefixchar = '%'
-
@prefixrexp = /^\%(.*?\r?\n)/
-
end
-
super(src, text)
-
end
-
-
end
-
-
-
##
-
## [experimental] allow header and footer in eRuby script
-
##
-
## ex.
-
## ====================
-
## ## without header and footer
-
## $ cat ex1.eruby
-
## <% def list_items(list) %>
-
## <% for item in list %>
-
## <li><%= item %></li>
-
## <% end %>
-
## <% end %>
-
##
-
## $ erubis -s ex1.eruby
-
## _buf = []; def list_items(list)
-
## ; for item in list
-
## ; _buf << '<li>'; _buf << ( item ).to_s; _buf << '</li>
-
## '; end
-
## ; end
-
## ;
-
## _buf.join
-
##
-
## ## with header and footer
-
## $ cat ex2.eruby
-
## <!--#header:
-
## def list_items(list)
-
## #-->
-
## <% for item in list %>
-
## <li><%= item %></li>
-
## <% end %>
-
## <!--#footer:
-
## end
-
## #-->
-
##
-
## $ erubis -s -c HeaderFooterEruby ex4.eruby
-
##
-
## def list_items(list)
-
## _buf = []; _buf << '
-
## '; for item in list
-
## ; _buf << '<li>'; _buf << ( item ).to_s; _buf << '</li>
-
## '; end
-
## ; _buf << '
-
## ';
-
## _buf.join
-
## end
-
##
-
## ====================
-
##
-
## this is language-independent.
-
##
-
1
module HeaderFooterEnhancer
-
-
1
def self.desc # :nodoc:
-
"allow header/footer in document (ex. '<!--#header: #-->')"
-
end
-
-
1
HEADER_FOOTER_PATTERN = /(.*?)(^[ \t]*)?<!--\#(\w+):(.*?)\#-->([ \t]*\r?\n)?/m
-
-
1
def add_text(src, text)
-
m = nil
-
text.scan(HEADER_FOOTER_PATTERN) do |txt, lspace, word, content, rspace|
-
m = Regexp.last_match
-
flag_trim = @trim && lspace && rspace
-
super(src, txt)
-
content = "#{lspace}#{content}#{rspace}" if flag_trim
-
super(src, lspace) if !flag_trim && lspace
-
instance_variable_set("@#{word}", content)
-
super(src, rspace) if !flag_trim && rspace
-
end
-
#rest = $' || text # ruby1.8
-
rest = m ? text[m.end(0)..-1] : text # ruby1.9
-
super(src, rest)
-
end
-
-
1
attr_accessor :header, :footer
-
-
1
def convert(input)
-
source = super
-
return @src = "#{@header}#{source}#{@footer}"
-
end
-
-
end
-
-
-
##
-
## delete indentation of HTML.
-
##
-
## this is language-independent.
-
##
-
1
module DeleteIndentEnhancer
-
-
1
def self.desc # :nodoc:
-
"delete indentation of HTML."
-
end
-
-
1
def convert_input(src, input)
-
input = input.gsub(/^[ \t]+</, '<')
-
super(src, input)
-
end
-
-
end
-
-
-
##
-
## convert "<h1><%=title%></h1>" into "_buf << %Q`<h1>#{title}</h1>`"
-
##
-
## this is only for Eruby.
-
##
-
1
module InterpolationEnhancer
-
-
1
def self.desc # :nodoc:
-
"convert '<p><%=text%></p>' into '_buf << %Q`<p>\#{text}</p>`'"
-
end
-
-
1
def convert_input(src, input)
-
pat = @pattern
-
regexp = pat.nil? || pat == '<% %>' ? Basic::Converter::DEFAULT_REGEXP : pattern_regexp(pat)
-
pos = 0
-
is_bol = true # is beginning of line
-
str = ''
-
input.scan(regexp) do |indicator, code, tailch, rspace|
-
match = Regexp.last_match()
-
len = match.begin(0) - pos
-
text = input[pos, len]
-
pos = match.end(0)
-
ch = indicator ? indicator[0] : nil
-
lspace = ch == ?= ? nil : detect_spaces_at_bol(text, is_bol)
-
is_bol = rspace ? true : false
-
_add_text_to_str(str, text)
-
## * when '<%= %>', do nothing
-
## * when '<% %>' or '<%# %>', delete spaces iff only spaces are around '<% %>'
-
if ch == ?= # <%= %>
-
rspace = nil if tailch && !tailch.empty?
-
str << lspace if lspace
-
add_expr(str, code, indicator)
-
str << rspace if rspace
-
elsif ch == ?\# # <%# %>
-
n = code.count("\n") + (rspace ? 1 : 0)
-
if @trim && lspace && rspace
-
add_text(src, str)
-
str = ''
-
add_stmt(src, "\n" * n)
-
else
-
str << lspace if lspace
-
add_text(src, str)
-
str = ''
-
add_stmt(src, "\n" * n)
-
str << rspace if rspace
-
end
-
else # <% %>
-
if @trim && lspace && rspace
-
add_text(src, str)
-
str = ''
-
add_stmt(src, "#{lspace}#{code}#{rspace}")
-
else
-
str << lspace if lspace
-
add_text(src, str)
-
str = ''
-
add_stmt(src, code)
-
str << rspace if rspace
-
end
-
end
-
end
-
#rest = $' || input # ruby1.8
-
rest = pos == 0 ? input : input[pos..-1] # ruby1.9
-
_add_text_to_str(str, rest)
-
add_text(src, str)
-
end
-
-
1
def add_text(src, text)
-
return if !text || text.empty?
-
#src << " _buf << %Q`" << text << "`;"
-
if text[-1] == ?\n
-
text[-1] = "\\n"
-
src << " #{@bufvar} << %Q`#{text}`\n"
-
else
-
src << " #{@bufvar} << %Q`#{text}`;"
-
end
-
end
-
-
1
def _add_text_to_str(str, text)
-
return if !text || text.empty?
-
str << text.gsub(/[`\#\\]/, '\\\\\&')
-
end
-
-
1
def add_expr_escaped(str, code)
-
str << "\#{#{escaped_expr(code)}}"
-
end
-
-
1
def add_expr_literal(str, code)
-
str << "\#{#{code}}"
-
end
-
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
1
module Erubis
-
-
-
##
-
## base error class
-
##
-
1
class ErubisError < StandardError
-
end
-
-
-
##
-
## raised when method or function is not supported
-
##
-
1
class NotSupportedError < ErubisError
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
1
require 'erubis/error'
-
1
require 'erubis/context'
-
-
-
1
module Erubis
-
-
1
EMPTY_BINDING = binding()
-
-
-
##
-
## evaluate code
-
##
-
1
module Evaluator
-
-
1
def self.supported_properties # :nodoc:
-
return []
-
end
-
-
1
attr_accessor :src, :filename
-
-
1
def init_evaluator(properties)
-
@filename = properties[:filename]
-
end
-
-
1
def result(*args)
-
raise NotSupportedError.new("evaluation of code except Ruby is not supported.")
-
end
-
-
1
def evaluate(*args)
-
raise NotSupportedError.new("evaluation of code except Ruby is not supported.")
-
end
-
-
end
-
-
-
##
-
## evaluator for Ruby
-
##
-
1
module RubyEvaluator
-
1
include Evaluator
-
-
1
def self.supported_properties # :nodoc:
-
list = Evaluator.supported_properties
-
return list
-
end
-
-
## eval(@src) with binding object
-
1
def result(_binding_or_hash=TOPLEVEL_BINDING)
-
_arg = _binding_or_hash
-
if _arg.is_a?(Hash)
-
_b = binding()
-
eval _arg.collect{|k,v| "#{k} = _arg[#{k.inspect}]; "}.join, _b
-
elsif _arg.is_a?(Binding)
-
_b = _arg
-
elsif _arg.nil?
-
_b = binding()
-
else
-
raise ArgumentError.new("#{self.class.name}#result(): argument should be Binding or Hash but passed #{_arg.class.name} object.")
-
end
-
return eval(@src, _b, (@filename || '(erubis'))
-
end
-
-
## invoke context.instance_eval(@src)
-
1
def evaluate(_context=Context.new)
-
_context = Context.new(_context) if _context.is_a?(Hash)
-
#return _context.instance_eval(@src, @filename || '(erubis)')
-
#@_proc ||= eval("proc { #{@src} }", Erubis::EMPTY_BINDING, @filename || '(erubis)')
-
@_proc ||= eval("proc { #{@src} }", binding(), @filename || '(erubis)')
-
return _context.instance_eval(&@_proc)
-
end
-
-
## if object is an Class or Module then define instance method to it,
-
## else define singleton method to it.
-
1
def def_method(object, method_name, filename=nil)
-
m = object.is_a?(Module) ? :module_eval : :instance_eval
-
object.__send__(m, "def #{method_name}; #{@src}; end", filename || @filename || '(erubis)')
-
end
-
-
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
1
require 'erubis/util'
-
-
1
module Erubis
-
-
-
##
-
## code generator, called by Converter module
-
##
-
1
module Generator
-
-
1
def self.supported_properties() # :nodoc:
-
return [
-
[:escapefunc, nil, "escape function name"],
-
]
-
end
-
-
1
attr_accessor :escapefunc
-
-
1
def init_generator(properties={})
-
@escapefunc = properties[:escapefunc]
-
end
-
-
-
## (abstract) escape text string
-
##
-
## ex.
-
## def escape_text(text)
-
## return text.dump
-
## # or return "'" + text.gsub(/['\\]/, '\\\\\&') + "'"
-
## end
-
1
def escape_text(text)
-
not_implemented
-
end
-
-
## return escaped expression code (ex. 'h(...)' or 'htmlspecialchars(...)')
-
1
def escaped_expr(code)
-
code.strip!
-
return "#{@escapefunc}(#{code})"
-
end
-
-
## (abstract) add @preamble to src
-
1
def add_preamble(src)
-
not_implemented
-
end
-
-
## (abstract) add text string to src
-
1
def add_text(src, text)
-
not_implemented
-
end
-
-
## (abstract) add statement code to src
-
1
def add_stmt(src, code)
-
not_implemented
-
end
-
-
## (abstract) add expression literal code to src. this is called by add_expr().
-
1
def add_expr_literal(src, code)
-
not_implemented
-
end
-
-
## (abstract) add escaped expression code to src. this is called by add_expr().
-
1
def add_expr_escaped(src, code)
-
not_implemented
-
end
-
-
## (abstract) add expression code to src for debug. this is called by add_expr().
-
1
def add_expr_debug(src, code)
-
not_implemented
-
end
-
-
## (abstract) add @postamble to src
-
1
def add_postamble(src)
-
not_implemented
-
end
-
-
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
-
1
module Erubis
-
-
##
-
## helper for xml
-
##
-
1
module XmlHelper
-
-
1
module_function
-
-
1
ESCAPE_TABLE = {
-
'&' => '&',
-
'<' => '<',
-
'>' => '>',
-
'"' => '"',
-
"'" => ''',
-
}
-
-
1
def escape_xml(value)
-
value.to_s.gsub(/[&<>"]/) { |s| ESCAPE_TABLE[s] } # or /[&<>"']/
-
#value.to_s.gsub(/[&<>"]/) { ESCAPE_TABLE[$&] }
-
end
-
-
1
def escape_xml2(value)
-
return value.to_s.gsub(/\&/,'&').gsub(/</,'<').gsub(/>/,'>').gsub(/"/,'"')
-
end
-
-
1
alias h escape_xml
-
1
alias html_escape escape_xml
-
-
1
def url_encode(str)
-
return str.gsub(/[^-_.a-zA-Z0-9]+/) { |s|
-
s.unpack('C*').collect { |i| "%%%02X" % i }.join
-
}
-
end
-
-
1
alias u url_encode
-
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
##
-
## you can add site-local settings here.
-
## this files is required by erubis.rb
-
##
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
1
module Kernel
-
-
##
-
## raise NotImplementedError
-
##
-
1
def not_implemented #:doc:
-
backtrace = caller()
-
method_name = (backtrace.shift =~ /`(\w+)'$/) && $1
-
mesg = "class #{self.class.name} must implement abstract method '#{method_name}()'."
-
#mesg = "#{self.class.name}##{method_name}() is not implemented."
-
err = NotImplementedError.new mesg
-
err.set_backtrace backtrace
-
raise err
-
end
-
1
private :not_implemented
-
-
end
-
1
require "execjs/module"
-
1
require "execjs/runtimes"
-
-
1
module ExecJS
-
1
self.runtime ||= Runtimes.autodetect
-
end
-
1
module ExecJS
-
1
class DisabledRuntime
-
1
def name
-
"Disabled"
-
end
-
-
1
def exec(source)
-
raise Error, "ExecJS disabled"
-
end
-
-
1
def eval(source)
-
raise Error, "ExecJS disabled"
-
end
-
-
1
def compile(source)
-
raise Error, "ExecJS disabled"
-
end
-
-
1
def available?
-
true
-
end
-
end
-
end
-
1
require "shellwords"
-
1
require "tempfile"
-
-
1
module ExecJS
-
1
class ExternalRuntime
-
1
class Context
-
1
def initialize(runtime, source = "")
-
source = source.encode('UTF-8') if source.respond_to?(:encode)
-
-
@runtime = runtime
-
@source = source
-
end
-
-
1
def eval(source, options = {})
-
source = source.encode('UTF-8') if source.respond_to?(:encode)
-
-
if /\S/ =~ source
-
exec("return eval(#{MultiJson.encode("(#{source})")})")
-
end
-
end
-
-
1
def exec(source, options = {})
-
source = source.encode('UTF-8') if source.respond_to?(:encode)
-
source = "#{@source}\n#{source}" if @source
-
-
compile_to_tempfile(source) do |file|
-
extract_result(@runtime.send(:exec_runtime, file.path))
-
end
-
end
-
-
1
def call(identifier, *args)
-
eval "#{identifier}.apply(this, #{MultiJson.encode(args)})"
-
end
-
-
1
protected
-
1
def compile_to_tempfile(source)
-
tempfile = Tempfile.open(['execjs', '.js'])
-
tempfile.write compile(source)
-
tempfile.close
-
yield tempfile
-
ensure
-
tempfile.close!
-
end
-
-
1
def compile(source)
-
@runtime.send(:runner_source).dup.tap do |output|
-
output.sub!('#{source}') do
-
source
-
end
-
output.sub!('#{encoded_source}') do
-
encoded_source = encode_unicode_codepoints(source)
-
MultiJson.encode("(function(){ #{encoded_source} })()")
-
end
-
output.sub!('#{json2_source}') do
-
IO.read(ExecJS.root + "/support/json2.js")
-
end
-
end
-
end
-
-
1
def extract_result(output)
-
status, value = output.empty? ? [] : MultiJson.decode(output)
-
if status == "ok"
-
value
-
elsif value =~ /SyntaxError:/
-
raise RuntimeError, value
-
else
-
raise ProgramError, value
-
end
-
end
-
-
1
if "".respond_to?(:codepoints)
-
1
def encode_unicode_codepoints(str)
-
str.gsub(/[\u0080-\uffff]/) do |ch|
-
"\\u%04x" % ch.codepoints.to_a
-
end
-
end
-
else
-
def encode_unicode_codepoints(str)
-
str.gsub(/([\xC0-\xDF][\x80-\xBF]|
-
[\xE0-\xEF][\x80-\xBF]{2}|
-
[\xF0-\xF7][\x80-\xBF]{3})+/nx) do |ch|
-
"\\u%04x" % ch.unpack("U*")
-
end
-
end
-
end
-
end
-
-
1
attr_reader :name
-
-
1
def initialize(options)
-
4
@name = options[:name]
-
4
@command = options[:command]
-
4
@runner_path = options[:runner_path]
-
4
@test_args = options[:test_args]
-
4
@test_match = options[:test_match]
-
4
@encoding = options[:encoding]
-
4
@binary = nil
-
end
-
-
1
def exec(source)
-
context = Context.new(self)
-
context.exec(source)
-
end
-
-
1
def eval(source)
-
context = Context.new(self)
-
context.eval(source)
-
end
-
-
1
def compile(source)
-
Context.new(self, source)
-
end
-
-
1
def available?
-
require "multi_json"
-
binary ? true : false
-
end
-
-
1
private
-
1
def binary
-
@binary ||= locate_binary
-
end
-
-
1
def locate_executable(cmd)
-
if ExecJS.windows? && File.extname(cmd) == ""
-
cmd << ".exe"
-
end
-
-
if File.executable? cmd
-
cmd
-
else
-
path = ENV['PATH'].split(File::PATH_SEPARATOR).find { |p|
-
full_path = File.join(p, cmd)
-
File.executable?(full_path) && File.file?(full_path)
-
}
-
path && File.expand_path(cmd, path)
-
end
-
end
-
-
1
protected
-
1
def runner_source
-
@runner_source ||= IO.read(@runner_path)
-
end
-
-
1
def exec_runtime(filename)
-
output = sh("#{shell_escape(*(binary.split(' ') << filename))} 2>&1")
-
if $?.success?
-
output
-
else
-
raise RuntimeError, output
-
end
-
end
-
-
1
def locate_binary
-
if binary = which(@command)
-
if @test_args
-
output = `#{shell_escape(binary, @test_args)} 2>&1`
-
binary if output.match(@test_match)
-
else
-
binary
-
end
-
end
-
end
-
-
1
def which(command)
-
Array(command).find do |name|
-
name, args = name.split(/\s+/, 2)
-
path = locate_executable(name)
-
-
next unless path
-
-
args ? "#{path} #{args}" : path
-
end
-
end
-
-
1
if "".respond_to?(:force_encoding)
-
1
def sh(command)
-
output, options = nil, {}
-
options[:external_encoding] = @encoding if @encoding
-
options[:internal_encoding] = Encoding.default_internal || 'UTF-8'
-
IO.popen(command, options) { |f| output = f.read }
-
output
-
end
-
else
-
require "iconv"
-
-
def sh(command)
-
output = nil
-
IO.popen(command) { |f| output = f.read }
-
-
if @encoding
-
Iconv.new('UTF-8', @encoding).iconv(output)
-
else
-
output
-
end
-
end
-
end
-
-
1
if ExecJS.windows?
-
def shell_escape(*args)
-
# see http://technet.microsoft.com/en-us/library/cc723564.aspx#XSLTsection123121120120
-
args.map { |arg|
-
arg = %Q("#{arg.gsub('"','""')}") if arg.match(/[&|()<>^ "]/)
-
arg
-
}.join(" ")
-
end
-
else
-
1
def shell_escape(*args)
-
Shellwords.join(args)
-
end
-
end
-
end
-
end
-
1
module ExecJS
-
1
class JohnsonRuntime
-
1
class Context
-
1
def initialize(source = "")
-
@runtime = Johnson::Runtime.new
-
@runtime.evaluate(source)
-
end
-
-
1
def exec(source, options = {})
-
source = source.encode('UTF-8') if source.respond_to?(:encode)
-
-
if /\S/ =~ source
-
eval "(function(){#{source}})()", options
-
end
-
end
-
-
1
def eval(source, options = {})
-
source = source.encode('UTF-8') if source.respond_to?(:encode)
-
-
if /\S/ =~ source
-
unbox @runtime.evaluate("(#{source})")
-
end
-
rescue Johnson::Error => e
-
if syntax_error?(e)
-
raise RuntimeError, e.message
-
else
-
raise ProgramError, e.message
-
end
-
end
-
-
1
def call(properties, *args)
-
unbox @runtime.evaluate(properties).call(*args)
-
rescue Johnson::Error => e
-
if syntax_error?(e)
-
raise RuntimeError, e.message
-
else
-
raise ProgramError, e.message
-
end
-
end
-
-
1
def unbox(value)
-
case
-
when function?(value)
-
nil
-
when string?(value)
-
value.respond_to?(:force_encoding) ?
-
value.force_encoding('UTF-8') :
-
value
-
when array?(value)
-
value.map { |v| unbox(v) }
-
when object?(value)
-
value.inject({}) do |vs, (k, v)|
-
vs[k] = unbox(v) unless function?(v)
-
vs
-
end
-
else
-
value
-
end
-
end
-
-
1
private
-
1
def syntax_error?(error)
-
error.message =~ /^syntax error at /
-
end
-
-
1
def function?(value)
-
value.respond_to?(:function?) && value.function?
-
end
-
-
1
def string?(value)
-
value.is_a?(String)
-
end
-
-
1
def array?(value)
-
array_test.call(value)
-
end
-
-
1
def object?(value)
-
value.respond_to?(:inject)
-
end
-
-
1
def array_test
-
@array_test ||= @runtime.evaluate("(function(a) {return a instanceof [].constructor})")
-
end
-
end
-
-
1
def name
-
"Johnson (SpiderMonkey)"
-
end
-
-
1
def exec(source)
-
context = Context.new
-
context.exec(source)
-
end
-
-
1
def eval(source)
-
context = Context.new
-
context.eval(source)
-
end
-
-
1
def compile(source)
-
Context.new(source)
-
end
-
-
1
def available?
-
require "johnson"
-
true
-
rescue LoadError
-
false
-
end
-
end
-
end
-
1
require "execjs/version"
-
1
require "rbconfig"
-
-
1
module ExecJS
-
1
class Error < ::StandardError; end
-
1
class RuntimeError < Error; end
-
1
class ProgramError < Error; end
-
1
class RuntimeUnavailable < RuntimeError; end
-
-
1
class << self
-
1
attr_reader :runtime
-
-
1
def runtime=(runtime)
-
1
raise RuntimeUnavailable, "#{runtime.name} is unavailable on this system" unless runtime.available?
-
1
@runtime = runtime
-
end
-
-
1
def exec(source)
-
runtime.exec(source)
-
end
-
-
1
def eval(source)
-
runtime.eval(source)
-
end
-
-
1
def compile(source)
-
runtime.compile(source)
-
end
-
-
1
def root
-
4
@root ||= File.expand_path("..", __FILE__)
-
end
-
-
1
def windows?
-
1
@windows ||= RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
-
end
-
end
-
end
-
1
module ExecJS
-
1
class MustangRuntime
-
1
class Context
-
1
def initialize(source = "")
-
source = source.encode('UTF-8') if source.respond_to?(:encode)
-
-
@v8_context = ::Mustang::Context.new
-
@v8_context.eval(source)
-
end
-
-
1
def exec(source, options = {})
-
source = source.encode('UTF-8') if source.respond_to?(:encode)
-
-
if /\S/ =~ source
-
eval "(function(){#{source}})()", options
-
end
-
end
-
-
1
def eval(source, options = {})
-
source = source.encode('UTF-8') if source.respond_to?(:encode)
-
-
if /\S/ =~ source
-
unbox @v8_context.eval("(#{source})")
-
end
-
end
-
-
1
def call(properties, *args)
-
unbox @v8_context.eval(properties).call(*args)
-
rescue NoMethodError => e
-
raise ProgramError, e.message
-
end
-
-
1
def unbox(value)
-
case value
-
when Mustang::V8::Array
-
value.map { |v| unbox(v) }
-
when Mustang::V8::Boolean
-
value.to_bool
-
when Mustang::V8::NullClass, Mustang::V8::UndefinedClass
-
nil
-
when Mustang::V8::Function
-
nil
-
when Mustang::V8::SyntaxError
-
raise RuntimeError, value.message
-
when Mustang::V8::Error
-
raise ProgramError, value.message
-
when Mustang::V8::Object
-
value.inject({}) { |h, (k, v)|
-
v = unbox(v)
-
h[k] = v if v
-
h
-
}
-
else
-
value.respond_to?(:delegate) ? value.delegate : value
-
end
-
end
-
end
-
-
1
def name
-
"Mustang (V8)"
-
end
-
-
1
def exec(source)
-
context = Context.new
-
context.exec(source)
-
end
-
-
1
def eval(source)
-
context = Context.new
-
context.eval(source)
-
end
-
-
1
def compile(source)
-
Context.new(source)
-
end
-
-
1
def available?
-
require "mustang"
-
true
-
rescue LoadError
-
false
-
end
-
end
-
end
-
1
module ExecJS
-
1
class RubyRacerRuntime
-
1
class Context
-
1
def initialize(source = "")
-
source = source.encode('UTF-8') if source.respond_to?(:encode)
-
-
lock do
-
@v8_context = ::V8::Context.new
-
@v8_context.eval(source)
-
end
-
end
-
-
1
def exec(source, options = {})
-
source = source.encode('UTF-8') if source.respond_to?(:encode)
-
-
if /\S/ =~ source
-
eval "(function(){#{source}})()", options
-
end
-
end
-
-
1
def eval(source, options = {})
-
source = source.encode('UTF-8') if source.respond_to?(:encode)
-
-
if /\S/ =~ source
-
lock do
-
begin
-
unbox @v8_context.eval("(#{source})")
-
rescue ::V8::JSError => e
-
if e.value["name"] == "SyntaxError"
-
raise RuntimeError, e.message
-
else
-
raise ProgramError, e.message
-
end
-
end
-
end
-
end
-
end
-
-
1
def call(properties, *args)
-
lock do
-
begin
-
unbox @v8_context.eval(properties).call(*args)
-
rescue ::V8::JSError => e
-
if e.value["name"] == "SyntaxError"
-
raise RuntimeError, e.message
-
else
-
raise ProgramError, e.message
-
end
-
end
-
end
-
end
-
-
1
def unbox(value)
-
case value
-
when ::V8::Function
-
nil
-
when ::V8::Array
-
value.map { |v| unbox(v) }
-
when ::V8::Object
-
value.inject({}) do |vs, (k, v)|
-
vs[k] = unbox(v) unless v.is_a?(::V8::Function)
-
vs
-
end
-
when String
-
value.respond_to?(:force_encoding) ?
-
value.force_encoding('UTF-8') :
-
value
-
else
-
value
-
end
-
end
-
-
1
private
-
1
def lock
-
result, exception = nil, nil
-
V8::C::Locker() do
-
begin
-
result = yield
-
rescue Exception => e
-
exception = e
-
end
-
end
-
-
if exception
-
raise exception
-
else
-
result
-
end
-
end
-
end
-
-
1
def name
-
"therubyracer (V8)"
-
end
-
-
1
def exec(source)
-
context = Context.new
-
context.exec(source)
-
end
-
-
1
def eval(source)
-
context = Context.new
-
context.eval(source)
-
end
-
-
1
def compile(source)
-
Context.new(source)
-
end
-
-
1
def available?
-
2
require "v8"
-
2
true
-
rescue LoadError
-
false
-
end
-
end
-
end
-
1
module ExecJS
-
1
class RubyRhinoRuntime
-
1
class Context
-
1
def initialize(source = "")
-
source = source.encode('UTF-8') if source.respond_to?(:encode)
-
-
@rhino_context = ::Rhino::Context.new
-
fix_memory_limit! @rhino_context
-
@rhino_context.eval(source)
-
end
-
-
1
def exec(source, options = {})
-
source = source.encode('UTF-8') if source.respond_to?(:encode)
-
-
if /\S/ =~ source
-
eval "(function(){#{source}})()", options
-
end
-
end
-
-
1
def eval(source, options = {})
-
source = source.encode('UTF-8') if source.respond_to?(:encode)
-
-
if /\S/ =~ source
-
unbox @rhino_context.eval("(#{source})")
-
end
-
rescue ::Rhino::JavascriptError => e
-
if e.message == "syntax error"
-
raise RuntimeError, e.message
-
else
-
raise ProgramError, e.message
-
end
-
end
-
-
1
def call(properties, *args)
-
unbox @rhino_context.eval(properties).call(*args)
-
rescue ::Rhino::JavascriptError => e
-
if e.message == "syntax error"
-
raise RuntimeError, e.message
-
else
-
raise ProgramError, e.message
-
end
-
end
-
-
1
def unbox(value)
-
case value = ::Rhino::To.ruby(value)
-
when ::Rhino::NativeFunction
-
nil
-
when ::Rhino::NativeObject
-
value.inject({}) do |vs, (k, v)|
-
case v
-
when ::Rhino::NativeFunction, ::Rhino::J::Function
-
nil
-
else
-
vs[k] = unbox(v)
-
end
-
vs
-
end
-
when Array
-
value.map { |v| unbox(v) }
-
else
-
value
-
end
-
end
-
-
1
private
-
# Disables bytecode compiling which limits you to 64K scripts
-
1
def fix_memory_limit!(context)
-
if context.respond_to?(:optimization_level=)
-
context.optimization_level = -1
-
else
-
context.instance_eval { @native.setOptimizationLevel(-1) }
-
end
-
end
-
end
-
-
1
def name
-
"therubyrhino (Rhino)"
-
end
-
-
1
def exec(source)
-
context = Context.new
-
context.exec(source)
-
end
-
-
1
def eval(source)
-
context = Context.new
-
context.eval(source)
-
end
-
-
1
def compile(source)
-
Context.new(source)
-
end
-
-
1
def available?
-
require "rhino"
-
true
-
rescue LoadError
-
false
-
end
-
end
-
end
-
1
require "execjs/module"
-
1
require "execjs/disabled_runtime"
-
1
require "execjs/external_runtime"
-
1
require "execjs/johnson_runtime"
-
1
require "execjs/mustang_runtime"
-
1
require "execjs/ruby_racer_runtime"
-
1
require "execjs/ruby_rhino_runtime"
-
-
1
module ExecJS
-
1
module Runtimes
-
1
Disabled = DisabledRuntime.new
-
-
1
RubyRacer = RubyRacerRuntime.new
-
-
1
RubyRhino = RubyRhinoRuntime.new
-
-
1
Johnson = JohnsonRuntime.new
-
-
1
Mustang = MustangRuntime.new
-
-
1
Node = ExternalRuntime.new(
-
:name => "Node.js (V8)",
-
:command => ["nodejs", "node"],
-
:runner_path => ExecJS.root + "/support/node_runner.js",
-
:encoding => 'UTF-8'
-
)
-
-
1
JavaScriptCore = ExternalRuntime.new(
-
:name => "JavaScriptCore",
-
:command => "/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc",
-
:runner_path => ExecJS.root + "/support/jsc_runner.js"
-
)
-
-
1
SpiderMonkey = Spidermonkey = ExternalRuntime.new(
-
:name => "SpiderMonkey",
-
:command => "js",
-
:runner_path => ExecJS.root + "/support/spidermonkey_runner.js"
-
)
-
-
1
JScript = ExternalRuntime.new(
-
:name => "JScript",
-
:command => "cscript //E:jscript //Nologo //U",
-
:runner_path => ExecJS.root + "/support/jscript_runner.js",
-
:encoding => 'UTF-16LE' # CScript with //U returns UTF-16LE
-
)
-
-
-
1
def self.autodetect
-
1
from_environment || best_available ||
-
raise(RuntimeUnavailable, "Could not find a JavaScript runtime. " +
-
"See https://github.com/sstephenson/execjs for a list of available runtimes.")
-
end
-
-
1
def self.best_available
-
1
runtimes.find(&:available?)
-
end
-
-
1
def self.from_environment
-
1
if name = ENV["EXECJS_RUNTIME"]
-
if runtime = const_get(name)
-
if runtime.available?
-
runtime if runtime.available?
-
else
-
raise RuntimeUnavailable, "#{runtime.name} runtime is not available on this system"
-
end
-
elsif !name.empty?
-
raise RuntimeUnavailable, "#{name} runtime is not defined"
-
end
-
end
-
end
-
-
1
def self.names
-
@names ||= constants.inject({}) { |h, name| h.merge(const_get(name) => name) }.values
-
end
-
-
1
def self.runtimes
-
@runtimes ||= [
-
RubyRacer,
-
RubyRhino,
-
Johnson,
-
Mustang,
-
Node,
-
JavaScriptCore,
-
SpiderMonkey,
-
JScript
-
1
]
-
end
-
end
-
-
1
def self.runtimes
-
Runtimes.runtimes
-
end
-
end
-
1
module ExecJS
-
1
VERSION = "1.3.0"
-
end
-
1
require "active_support/core_ext/module/delegation"
-
-
1
require 'factory_girl/errors'
-
1
require 'factory_girl/factory_runner'
-
1
require 'factory_girl/strategy_calculator'
-
1
require "factory_girl/strategy/build"
-
1
require "factory_girl/strategy/create"
-
1
require "factory_girl/strategy/attributes_for"
-
1
require "factory_girl/strategy/stub"
-
1
require "factory_girl/strategy/null"
-
1
require 'factory_girl/registry'
-
1
require 'factory_girl/null_factory'
-
1
require 'factory_girl/null_object'
-
1
require 'factory_girl/evaluation'
-
1
require 'factory_girl/factory'
-
1
require 'factory_girl/attribute_assigner'
-
1
require 'factory_girl/evaluator'
-
1
require 'factory_girl/evaluator_class_definer'
-
1
require 'factory_girl/attribute'
-
1
require 'factory_girl/callback'
-
1
require 'factory_girl/callback_runner'
-
1
require 'factory_girl/declaration_list'
-
1
require 'factory_girl/declaration'
-
1
require 'factory_girl/sequence'
-
1
require 'factory_girl/attribute_list'
-
1
require 'factory_girl/trait'
-
1
require 'factory_girl/aliases'
-
1
require 'factory_girl/definition'
-
1
require 'factory_girl/definition_proxy'
-
1
require 'factory_girl/syntax'
-
1
require 'factory_girl/find_definitions'
-
1
require 'factory_girl/reload'
-
1
require 'factory_girl/version'
-
-
1
module FactoryGirl
-
1
def self.factories
-
7
@factories ||= Registry.new("Factory")
-
end
-
-
1
def self.register_factory(factory)
-
7
factories.add(factory)
-
end
-
-
1
def self.factory_by_name(name)
-
factories.find(name)
-
end
-
-
1
def self.sequences
-
@sequences ||= Registry.new("Sequence")
-
end
-
-
1
def self.register_sequence(sequence)
-
sequences.add(sequence)
-
end
-
-
1
def self.sequence_by_name(name)
-
sequences.find(name)
-
end
-
-
1
def self.traits
-
@traits ||= Registry.new("Trait")
-
end
-
-
1
def self.register_trait(trait)
-
traits.add(trait)
-
end
-
-
1
def self.trait_by_name(name)
-
traits.find(name)
-
end
-
-
1
def self.callback_names
-
24
[:after_build, :after_create, :after_stub, :before_create].freeze
-
end
-
end
-
1
module FactoryGirl
-
1
class << self
-
1
attr_accessor :aliases #:nodoc:
-
end
-
-
1
self.aliases = [
-
[/(.+)_id/, '\1'],
-
[/(.*)/, '\1_id']
-
]
-
-
1
def self.aliases_for(attribute) #:nodoc:
-
aliases.collect do |params|
-
pattern, replace = *params
-
if pattern.match(attribute.to_s)
-
attribute.to_s.sub(pattern, replace).to_sym
-
end
-
end.compact << attribute
-
end
-
end
-
1
require "factory_girl/attribute/static"
-
1
require "factory_girl/attribute/dynamic"
-
1
require "factory_girl/attribute/association"
-
1
require "factory_girl/attribute/sequence"
-
-
1
module FactoryGirl
-
-
1
class Attribute #:nodoc:
-
1
attr_reader :name, :ignored
-
-
1
def initialize(name, ignored)
-
@name = name.to_sym
-
@ignored = ignored
-
ensure_non_attribute_writer!
-
end
-
-
1
def to_proc
-
lambda { }
-
end
-
-
1
def association?
-
false
-
end
-
-
1
def alias_for?(attr)
-
FactoryGirl.aliases_for(attr).include?(name)
-
end
-
-
1
private
-
-
1
def ensure_non_attribute_writer!
-
if @name.to_s =~ /=$/
-
attribute_name = $`
-
raise AttributeDefinitionError,
-
"factory_girl uses 'f.#{attribute_name} value' syntax " +
-
"rather than 'f.#{attribute_name} = value'"
-
end
-
end
-
end
-
end
-
1
module FactoryGirl
-
1
class Attribute #:nodoc:
-
1
class Association < Attribute #:nodoc:
-
1
attr_reader :factory
-
-
1
def initialize(name, factory, overrides)
-
super(name, false)
-
@factory = factory
-
@overrides = overrides
-
end
-
-
1
def to_proc
-
factory = @factory
-
overrides = @overrides
-
lambda { association(factory, overrides) }
-
end
-
-
1
def association?
-
true
-
end
-
end
-
end
-
end
-
1
module FactoryGirl
-
1
class Attribute #:nodoc:
-
1
class Dynamic < Attribute #:nodoc:
-
1
def initialize(name, ignored, block)
-
super(name, ignored)
-
@block = block
-
end
-
-
1
def to_proc
-
block = @block
-
-
lambda {
-
value = block.arity == 1 ? block.call(self) : instance_exec(&block)
-
raise SequenceAbuseError if FactoryGirl::Sequence === value
-
value
-
}
-
end
-
end
-
end
-
end
-
1
module FactoryGirl
-
1
class Attribute
-
-
1
class Sequence < Attribute
-
1
def initialize(name, sequence, ignored)
-
super(name, ignored)
-
@sequence = sequence
-
end
-
-
1
def to_proc
-
sequence = @sequence
-
lambda { FactoryGirl.generate(sequence) }
-
end
-
end
-
-
end
-
end
-
1
module FactoryGirl
-
1
class Attribute #:nodoc:
-
1
class Static < Attribute #:nodoc:
-
1
def initialize(name, value, ignored)
-
super(name, ignored)
-
@value = value
-
end
-
-
1
def to_proc
-
value = @value
-
lambda { value }
-
end
-
end
-
end
-
end
-
1
module FactoryGirl
-
1
class AttributeAssigner
-
1
def initialize(evaluator, &instance_builder)
-
@instance_builder = instance_builder
-
@evaluator = evaluator
-
@attribute_list = evaluator.class.attribute_list
-
@attribute_names_assigned = []
-
end
-
-
1
def object
-
@evaluator.instance = build_class_instance
-
build_class_instance.tap do |instance|
-
attributes_to_set_on_instance.each do |attribute|
-
instance.send("#{attribute}=", get(attribute))
-
@attribute_names_assigned << attribute
-
end
-
end
-
end
-
-
1
def hash
-
@evaluator.instance = NullObject.new
-
-
attributes_to_set_on_hash.inject({}) do |result, attribute|
-
result[attribute] = get(attribute)
-
result
-
end
-
end
-
-
1
private
-
-
1
def build_class_instance
-
@build_class_instance ||= @evaluator.instance_exec(&@instance_builder)
-
end
-
-
1
def get(attribute_name)
-
@evaluator.send(attribute_name)
-
end
-
-
1
def attributes_to_set_on_instance
-
(attribute_names_to_assign - @attribute_names_assigned).uniq
-
end
-
-
1
def attributes_to_set_on_hash
-
attribute_names_to_assign - association_names
-
end
-
-
1
def attribute_names_to_assign
-
non_ignored_attribute_names + override_names - ignored_attribute_names - alias_names_to_ignore
-
end
-
-
1
def non_ignored_attribute_names
-
@attribute_list.reject(&:ignored).map(&:name)
-
end
-
-
1
def ignored_attribute_names
-
@attribute_list.select(&:ignored).map(&:name)
-
end
-
-
1
def association_names
-
@attribute_list.associations.map(&:name)
-
end
-
-
1
def override_names
-
@evaluator.__overrides.keys
-
end
-
-
1
def alias_names_to_ignore
-
@attribute_list.reject(&:ignored).map do |attribute|
-
override_names.map {|override| attribute.name if attribute.alias_for?(override) && attribute.name != override && !ignored_attribute_names.include?(override) }
-
end.flatten.compact
-
end
-
end
-
end
-
1
module FactoryGirl
-
1
class AttributeList
-
1
include Enumerable
-
-
1
def initialize(name = nil)
-
@name = name
-
@attributes = []
-
end
-
-
1
def define_attribute(attribute)
-
ensure_attribute_not_self_referencing! attribute
-
ensure_attribute_not_defined! attribute
-
-
add_attribute attribute
-
end
-
-
1
def each(&block)
-
@attributes.each(&block)
-
end
-
-
1
def associations
-
@attributes.select(&:association?)
-
end
-
-
1
def apply_attributes(attributes_to_apply)
-
attributes_to_apply.each {|attribute| add_attribute(attribute) }
-
end
-
-
1
private
-
-
1
def add_attribute(attribute)
-
@attributes << attribute
-
attribute
-
end
-
-
1
def ensure_attribute_not_defined!(attribute)
-
if attribute_defined?(attribute.name)
-
raise AttributeDefinitionError, "Attribute already defined: #{attribute.name}"
-
end
-
end
-
-
1
def ensure_attribute_not_self_referencing!(attribute)
-
if attribute.respond_to?(:factory) && attribute.factory == @name
-
raise AssociationDefinitionError, "Self-referencing association '#{attribute.name}' in '#{attribute.factory}'"
-
end
-
end
-
-
1
def attribute_defined?(attribute_name)
-
@attributes.any? do |attribute|
-
attribute.name == attribute_name
-
end
-
end
-
end
-
end
-
1
module FactoryGirl
-
1
class Callback
-
1
attr_reader :name, :block
-
-
1
def initialize(name, block)
-
@name = name.to_sym
-
@block = block
-
check_name
-
end
-
-
1
def run(instance, evaluator)
-
case block.arity
-
when 1 then block.call(instance)
-
when 2 then block.call(instance, evaluator)
-
else block.call
-
end
-
end
-
-
1
def ==(other)
-
name == other.name &&
-
block == other.block
-
end
-
-
1
private
-
-
1
def check_name
-
unless FactoryGirl.callback_names.include?(name)
-
raise InvalidCallbackNameError, "#{name} is not a valid callback name. " +
-
"Valid callback names are #{FactoryGirl.callback_names.inspect}"
-
end
-
end
-
end
-
end
-
1
module FactoryGirl
-
1
class CallbackRunner
-
1
def initialize(callbacks, evaluator)
-
@callbacks = callbacks
-
@evaluator = evaluator
-
end
-
-
1
def update(name, result_instance)
-
callbacks_by_name(name).each do |callback|
-
callback.run(result_instance, @evaluator)
-
end
-
end
-
-
1
private
-
-
1
def callbacks_by_name(name)
-
@callbacks.select {|callback| callback.name == name }
-
end
-
end
-
end
-
1
require "factory_girl/declaration/static"
-
1
require "factory_girl/declaration/dynamic"
-
1
require "factory_girl/declaration/association"
-
1
require "factory_girl/declaration/implicit"
-
-
1
module FactoryGirl
-
1
class Declaration
-
1
attr_reader :name
-
-
1
def initialize(name, ignored = false)
-
24
@name = name
-
24
@ignored = ignored
-
end
-
-
1
def to_attributes
-
build
-
end
-
-
1
protected
-
1
attr_reader :ignored
-
end
-
end
-
1
module FactoryGirl
-
1
class Declaration
-
1
class Association < Declaration
-
1
def initialize(name, options)
-
super(name, false)
-
@options = options
-
end
-
-
1
def ==(other)
-
name == other.name &&
-
options == other.options
-
end
-
-
1
protected
-
1
attr_reader :options
-
-
1
private
-
-
1
def build
-
factory_name = @options.delete(:factory) || name
-
[Attribute::Association.new(name, factory_name, @options)]
-
end
-
end
-
end
-
end
-
1
module FactoryGirl
-
1
class Declaration
-
1
class Dynamic < Declaration
-
1
def initialize(name, ignored = false, block = nil)
-
super(name, ignored)
-
@block = block
-
end
-
-
1
def ==(other)
-
name == other.name &&
-
ignored == other.ignored &&
-
block == other.block
-
end
-
-
1
protected
-
1
attr_reader :block
-
-
1
private
-
-
1
def build
-
[Attribute::Dynamic.new(name, @ignored, @block)]
-
end
-
end
-
end
-
end
-
1
module FactoryGirl
-
1
class Declaration
-
1
class Implicit < Declaration
-
1
def initialize(name, factory = nil, ignored = false)
-
super(name, ignored)
-
@factory = factory
-
end
-
-
1
def ==(other)
-
name == other.name &&
-
factory == other.factory &&
-
ignored == other.ignored
-
end
-
-
1
protected
-
1
attr_reader :factory
-
-
1
private
-
-
1
def build
-
if FactoryGirl.factories.registered?(name)
-
[Attribute::Association.new(name, name, {})]
-
elsif FactoryGirl.sequences.registered?(name)
-
[Attribute::Sequence.new(name, name, @ignored)]
-
else
-
@factory.inherit_traits([name])
-
[]
-
end
-
end
-
end
-
end
-
end
-
1
module FactoryGirl
-
1
class Declaration
-
1
class Static < Declaration
-
1
def initialize(name, value, ignored = false)
-
24
super(name, ignored)
-
24
@value = value
-
end
-
-
1
def ==(other)
-
name == other.name &&
-
value == other.value &&
-
ignored == other.ignored
-
end
-
-
1
protected
-
1
attr_reader :value
-
-
1
private
-
-
1
def build
-
[Attribute::Static.new(name, @value, @ignored)]
-
end
-
end
-
end
-
end
-
1
module FactoryGirl
-
1
class DeclarationList
-
1
include Enumerable
-
-
1
def initialize(name = nil)
-
7
@declarations = []
-
7
@name = name
-
7
@overridable = false
-
end
-
-
1
def declare_attribute(declaration)
-
24
delete_declaration(declaration) if overridable?
-
-
24
@declarations << declaration
-
24
declaration
-
end
-
-
1
def overridable
-
@overridable = true
-
end
-
-
1
def attribute_list
-
AttributeList.new(@name).tap do |list|
-
to_attributes.each do |attribute|
-
list.define_attribute(attribute)
-
end
-
end
-
end
-
-
1
def each(&block)
-
@declarations.each(&block)
-
end
-
-
1
private
-
-
1
def delete_declaration(declaration)
-
@declarations.delete_if {|decl| decl.name == declaration.name }
-
end
-
-
1
def to_attributes
-
@declarations.inject([]) {|result, declaration| result += declaration.to_attributes }
-
end
-
-
1
def overridable?
-
24
@overridable
-
end
-
end
-
end
-
1
module FactoryGirl
-
1
class Definition
-
1
attr_reader :callbacks, :defined_traits, :declarations, :constructor
-
-
1
def initialize(name = nil, base_traits = [])
-
7
@declarations = DeclarationList.new(name)
-
7
@callbacks = []
-
7
@defined_traits = []
-
7
@to_create = lambda {|instance| instance.save! }
-
7
@base_traits = base_traits
-
7
@additional_traits = []
-
7
@constructor = nil
-
end
-
-
1
delegate :declare_attribute, to: :declarations
-
-
1
def attributes
-
@attributes ||= declarations.attribute_list
-
end
-
-
1
def compile
-
attributes
-
end
-
-
1
def processing_order
-
base_traits + [self] + additional_traits
-
end
-
-
1
def overridable
-
declarations.overridable
-
self
-
end
-
-
1
def inherit_traits(new_traits)
-
@additional_traits += new_traits
-
end
-
-
1
def add_callback(callback)
-
@callbacks << callback
-
end
-
-
1
def to_create(&block)
-
if block_given?
-
@to_create = block
-
else
-
@to_create
-
end
-
end
-
-
1
def define_trait(trait)
-
@defined_traits << trait
-
end
-
-
1
def define_constructor(&block)
-
@constructor = block
-
end
-
-
1
private
-
-
1
def base_traits
-
@base_traits.map { |name| trait_by_name(name) }
-
end
-
-
1
def additional_traits
-
@additional_traits.map { |name| trait_by_name(name) }
-
end
-
-
1
def trait_by_name(name)
-
trait_for(name) || FactoryGirl.trait_by_name(name)
-
end
-
-
1
def trait_for(name)
-
defined_traits.detect {|trait| trait.name == name }
-
end
-
end
-
end
-
1
module FactoryGirl
-
1
class DefinitionProxy
-
1
UNPROXIED_METHODS = %w(__send__ __id__ nil? send object_id extend instance_eval initialize block_given? raise)
-
-
1
(instance_methods + private_instance_methods).each do |method|
-
174
undef_method(method) unless UNPROXIED_METHODS.include?(method.to_s)
-
end
-
-
1
attr_reader :child_factories
-
-
1
def initialize(definition, ignore = false)
-
7
@definition = definition
-
7
@ignore = ignore
-
7
@child_factories = []
-
end
-
-
# Adds an attribute that should be assigned on generated instances for this
-
# factory.
-
#
-
# This method should be called with either a value or block, but not both. If
-
# called with a block, the attribute will be generated "lazily," whenever an
-
# instance is generated. Lazy attribute blocks will not be called if that
-
# attribute is overridden for a specific instance.
-
#
-
# When defining lazy attributes, an instance of FactoryGirl::Strategy will
-
# be yielded, allowing associations to be built using the correct build
-
# strategy.
-
#
-
# Arguments:
-
# * name: +Symbol+ or +String+
-
# The name of this attribute. This will be assigned using "name=" for
-
# generated instances.
-
# * value: +Object+
-
# If no block is given, this value will be used for this attribute.
-
1
def add_attribute(name, value = nil, &block)
-
24
raise AttributeDefinitionError, "Both value and block given" if value && block_given?
-
-
24
declaration = if block_given?
-
Declaration::Dynamic.new(name, @ignore, block)
-
else
-
24
Declaration::Static.new(name, value, @ignore)
-
end
-
-
24
@definition.declare_attribute(declaration)
-
end
-
-
1
def ignore(&block)
-
proxy = DefinitionProxy.new(@definition, true)
-
proxy.instance_eval(&block)
-
end
-
-
# Calls add_attribute using the missing method name as the name of the
-
# attribute, so that:
-
#
-
# factory :user do
-
# name 'Billy Idol'
-
# end
-
#
-
# and:
-
#
-
# factory :user do
-
# add_attribute :name, 'Billy Idol'
-
# end
-
#
-
# are equivalent.
-
#
-
# If no argument or block is given, factory_girl will look for a sequence
-
# or association with the same name. This means that:
-
#
-
# factory :user do
-
# email { create(:email) }
-
# association :account
-
# end
-
#
-
# and:
-
#
-
# factory :user do
-
# email
-
# account
-
# end
-
#
-
# are equivalent.
-
1
def method_missing(name, *args, &block)
-
24
if args.empty? && block.nil?
-
@definition.declare_attribute(Declaration::Implicit.new(name, @definition, @ignore))
-
24
elsif args.first.is_a?(Hash) && args.first.has_key?(:factory)
-
association(name, *args)
-
24
elsif FactoryGirl.callback_names.include?(name)
-
@definition.add_callback(Callback.new(name, block))
-
else
-
24
add_attribute(name, *args, &block)
-
end
-
end
-
-
# Adds an attribute that will have unique values generated by a sequence with
-
# a specified format.
-
#
-
# The result of:
-
# factory :user do
-
# sequence(:email) { |n| "person#{n}@example.com" }
-
# end
-
#
-
# Is equal to:
-
# sequence(:email) { |n| "person#{n}@example.com" }
-
#
-
# factory :user do
-
# email { FactoryGirl.create(:email) }
-
# end
-
#
-
# Except that no globally available sequence will be defined.
-
1
def sequence(name, *args, &block)
-
sequence = Sequence.new(name, *args, &block)
-
add_attribute(name) { sequence.next }
-
end
-
-
# Adds an attribute that builds an association. The associated instance will
-
# be built using the same build strategy as the parent instance.
-
#
-
# Example:
-
# factory :user do
-
# name 'Joey'
-
# end
-
#
-
# factory :post do
-
# association :author, factory: :user
-
# end
-
#
-
# Arguments:
-
# * name: +Symbol+
-
# The name of this attribute.
-
# * options: +Hash+
-
#
-
# Options:
-
# * factory: +Symbol+ or +String+
-
# The name of the factory to use when building the associated instance.
-
# If no name is given, the name of the attribute is assumed to be the
-
# name of the factory. For example, a "user" association will by
-
# default use the "user" factory.
-
1
def association(name, options = {})
-
@definition.declare_attribute(Declaration::Association.new(name, options))
-
end
-
-
1
def to_create(&block)
-
@definition.to_create(&block)
-
end
-
-
1
def factory(name, options = {}, &block)
-
@child_factories << [name, options, block]
-
end
-
-
1
def trait(name, &block)
-
@definition.define_trait(Trait.new(name, &block))
-
end
-
-
1
def initialize_with(&block)
-
@definition.define_constructor(&block)
-
end
-
end
-
end
-
1
module FactoryGirl
-
# Raised when a factory is defined that attempts to instantiate itself.
-
1
class AssociationDefinitionError < RuntimeError; end
-
-
# Raised when a callback is defined that has an invalid name
-
1
class InvalidCallbackNameError < RuntimeError; end
-
-
# Raised when a factory is defined with the same name as a previously-defined factory.
-
1
class DuplicateDefinitionError < RuntimeError; end
-
-
# Raised when attempting to register a sequence from a dynamic attribute block
-
1
class SequenceAbuseError < RuntimeError; end
-
-
# Raised when defining an invalid attribute:
-
# * Defining an attribute which has a name ending in "="
-
# * Defining an attribute with both a static and lazy value
-
# * Defining an attribute twice in the same factory
-
1
class AttributeDefinitionError < RuntimeError; end
-
end
-
1
require "observer"
-
-
1
module FactoryGirl
-
1
class Evaluation
-
1
include Observable
-
-
1
def initialize(attribute_assigner, to_create)
-
@attribute_assigner = attribute_assigner
-
@to_create = to_create
-
end
-
-
1
def create(result_instance)
-
@to_create[result_instance]
-
end
-
-
1
delegate :object, :hash, to: :@attribute_assigner
-
-
1
def notify(name, result_instance)
-
changed
-
notify_observers(name, result_instance)
-
end
-
end
-
end
-
1
require "active_support/core_ext/hash/except"
-
1
require "active_support/core_ext/class/attribute"
-
-
1
module FactoryGirl
-
1
class Evaluator
-
1
class_attribute :attribute_lists
-
-
1
def self.attribute_list
-
AttributeList.new.tap do |list|
-
attribute_lists.each do |attribute_list|
-
list.apply_attributes attribute_list.to_a
-
end
-
end
-
end
-
-
1
private_instance_methods.each do |method|
-
83
undef_method(method) unless method =~ /^__|initialize/
-
end
-
-
1
def initialize(build_strategy, overrides = {})
-
@build_strategy = build_strategy
-
@overrides = overrides
-
@cached_attributes = overrides
-
-
@overrides.each do |name, value|
-
singleton_class.send :define_method, name, lambda { value }
-
end
-
end
-
-
1
def association(factory_name, overrides = {})
-
build_strategy = if overrides.has_key?(:strategy)
-
overrides[:strategy]
-
else
-
Strategy::Create
-
end
-
-
build_strategy = StrategyCalculator.new(build_strategy).strategy
-
runner = FactoryRunner.new(factory_name, build_strategy, [overrides.except(:strategy)])
-
@build_strategy.association(runner)
-
end
-
-
1
def instance=(object_instance)
-
@instance = object_instance
-
end
-
-
1
def method_missing(method_name, *args, &block)
-
if @cached_attributes.key?(method_name)
-
@cached_attributes[method_name]
-
else
-
@instance.send(method_name, *args, &block)
-
end
-
end
-
-
1
def __overrides
-
@overrides
-
end
-
end
-
end
-
1
module FactoryGirl
-
1
class EvaluatorClassDefiner
-
1
def initialize(attributes, parent_class)
-
@parent_class = parent_class
-
@attributes = attributes
-
-
attributes.each do |attribute|
-
define_attribute(attribute.name, attribute.to_proc)
-
end
-
end
-
-
1
def evaluator_class
-
@evaluator_class ||= Class.new(@parent_class).tap do |klass|
-
klass.attribute_lists ||= []
-
klass.attribute_lists += [@attributes]
-
end
-
end
-
-
1
private
-
-
1
def define_attribute(attribute_name, attribute_proc)
-
evaluator_class.send(:define_method, attribute_name) do
-
if @cached_attributes.key?(attribute_name)
-
@cached_attributes[attribute_name]
-
else
-
@cached_attributes[attribute_name] = instance_exec(&attribute_proc)
-
end
-
end
-
end
-
end
-
end
-
1
require "active_support/core_ext/hash/keys"
-
1
require "active_support/inflector"
-
-
1
module FactoryGirl
-
1
class Factory
-
1
attr_reader :name, :definition #:nodoc:
-
-
1
def initialize(name, options = {}) #:nodoc:
-
7
assert_valid_options(options)
-
7
@name = name.is_a?(Symbol) ? name : name.to_s.underscore.to_sym
-
7
@parent = options[:parent]
-
7
@aliases = options[:aliases] || []
-
7
@class_name = options[:class]
-
7
@definition = Definition.new(@name, options[:traits] || [])
-
7
@compiled = false
-
end
-
-
1
delegate :add_callback, :declare_attribute, :to_create, :define_trait,
-
:defined_traits, :inherit_traits, :processing_order, to: :@definition
-
-
1
def build_class #:nodoc:
-
@build_class ||= if class_name.is_a? Class
-
class_name
-
else
-
class_name.to_s.camelize.constantize
-
end
-
end
-
-
1
def run(strategy_class, overrides, &block) #:nodoc:
-
block ||= lambda {|result| result }
-
compile
-
-
strategy = strategy_class.new
-
-
evaluator = evaluator_class.new(strategy, overrides.symbolize_keys)
-
attribute_assigner = AttributeAssigner.new(evaluator, &instance_builder)
-
-
evaluation = Evaluation.new(attribute_assigner, to_create)
-
evaluation.add_observer(CallbackRunner.new(callbacks, evaluator))
-
-
strategy.result(evaluation).tap(&block)
-
end
-
-
1
def human_names
-
names.map {|name| name.to_s.humanize.downcase }
-
end
-
-
1
def associations
-
evaluator_class.attribute_list.associations
-
end
-
-
# Names for this factory, including aliases.
-
#
-
# Example:
-
#
-
# factory :user, aliases: [:author] do
-
# # ...
-
# end
-
#
-
# FactoryGirl.create(:author).class
-
# # => User
-
#
-
# Because an attribute defined without a value or block will build an
-
# association with the same name, this allows associations to be defined
-
# without factories, such as:
-
#
-
# factory :user, aliases: [:author] do
-
# # ...
-
# end
-
#
-
# factory :post do
-
# author
-
# end
-
#
-
# FactoryGirl.create(:post).author.class
-
# # => User
-
1
def names
-
7
[name] + @aliases
-
end
-
-
1
def compile
-
unless @compiled
-
parent.compile
-
parent.defined_traits.each {|trait| define_trait(trait) }
-
@definition.compile
-
@compiled = true
-
end
-
end
-
-
1
def with_traits(traits)
-
self.clone.tap do |factory_with_traits|
-
factory_with_traits.inherit_traits traits
-
end
-
end
-
-
1
protected
-
-
1
def class_name #:nodoc:
-
@class_name || parent.class_name || name
-
end
-
-
1
def evaluator_class
-
@evaluator_class ||= EvaluatorClassDefiner.new(attributes, parent.evaluator_class).evaluator_class
-
end
-
-
1
def attributes
-
compile
-
AttributeList.new(@name).tap do |list|
-
processing_order.each do |factory|
-
list.apply_attributes factory.attributes
-
end
-
end
-
end
-
-
1
def callbacks
-
parent.callbacks + processing_order.map {|factory| factory.callbacks }.flatten
-
end
-
-
1
def constructor
-
@constructor ||= @definition.constructor || parent.constructor
-
end
-
-
1
private
-
-
1
def assert_valid_options(options)
-
7
options.assert_valid_keys(:class, :parent, :aliases, :traits)
-
end
-
-
1
def parent
-
if @parent
-
FactoryGirl.factory_by_name(@parent)
-
else
-
NullFactory.new
-
end
-
end
-
-
1
def instance_builder
-
build_class = self.build_class
-
constructor || lambda { build_class.new }
-
end
-
-
1
def initialize_copy(source)
-
super
-
@definition = @definition.clone
-
@evaluator_class = nil
-
end
-
end
-
end
-
1
module FactoryGirl
-
1
class FactoryRunner
-
1
def initialize(name, strategy, traits_and_overrides)
-
@name = name
-
@strategy = strategy
-
-
@overrides = traits_and_overrides.extract_options!
-
@traits = traits_and_overrides
-
end
-
-
1
def run(strategy_override = nil, &block)
-
strategy_override ||= @strategy
-
factory = FactoryGirl.factory_by_name(@name)
-
-
factory.compile
-
-
if @traits.any?
-
factory = factory.with_traits(@traits)
-
end
-
-
factory.run(strategy_override, @overrides, &block)
-
end
-
end
-
end
-
1
module FactoryGirl
-
-
1
class << self
-
# An Array of strings specifying locations that should be searched for
-
# factory definitions. By default, factory_girl will attempt to require
-
# "factories," "test/factories," and "spec/factories." Only the first
-
# existing file will be loaded.
-
1
attr_accessor :definition_file_paths
-
end
-
1
self.definition_file_paths = %w(factories test/factories spec/factories)
-
-
1
def self.find_definitions #:nodoc:
-
4
absolute_definition_file_paths = definition_file_paths.map {|path| File.expand_path(path) }
-
-
1
absolute_definition_file_paths.uniq.each do |path|
-
3
load("#{path}.rb") if File.exists?("#{path}.rb")
-
-
3
if File.directory? path
-
1
Dir[File.join(path, '**', '*.rb')].sort.each do |file|
-
3
load file
-
end
-
end
-
end
-
end
-
end
-
1
module FactoryGirl
-
1
class NullFactory
-
1
attr_reader :definition
-
-
1
def initialize
-
@definition = Definition.new
-
end
-
-
1
delegate :defined_traits, :callbacks, :attributes, :constructor, to: :definition
-
-
1
def compile; end
-
1
def class_name; end
-
1
def evaluator_class; FactoryGirl::Evaluator; end
-
end
-
end
-
1
module FactoryGirl
-
1
class NullObject < ::BasicObject
-
1
def method_missing(*args)
-
nil
-
end
-
end
-
end
-
1
module FactoryGirl
-
1
class Registry
-
1
include Enumerable
-
-
1
def initialize(name)
-
1
@name = name
-
1
@items = {}
-
end
-
-
1
def add(item)
-
14
item.names.each { |name| add_as(name, item) }
-
7
item
-
end
-
-
1
def find(name)
-
@items[name.to_sym] or raise ArgumentError.new("#{@name} not registered: #{name.to_s}")
-
end
-
-
1
def each(&block)
-
@items.values.uniq.each(&block)
-
end
-
-
1
def [](name)
-
find(name)
-
end
-
-
1
def registered?(name)
-
7
@items.key?(name.to_sym)
-
end
-
-
1
def clear
-
@items.clear
-
end
-
-
1
private
-
-
1
def add_as(name, item)
-
7
if registered?(name)
-
raise DuplicateDefinitionError, "#{@name} already registered: #{name}"
-
else
-
7
@items[name.to_sym] = item
-
end
-
end
-
end
-
end
-
-
1
module FactoryGirl
-
1
def self.reload
-
self.factories.clear
-
self.sequences.clear
-
self.traits.clear
-
self.find_definitions
-
end
-
end
-
1
module FactoryGirl
-
-
# Sequences are defined using sequence within a FactoryGirl.define block.
-
# Sequence values are generated using next.
-
1
class Sequence
-
1
attr_reader :name
-
-
1
def initialize(name, *args, &proc) #:nodoc:
-
@name = name
-
@proc = proc
-
-
options = args.extract_options!
-
@value = args.first || 1
-
@aliases = options[:aliases] || []
-
end
-
-
1
def next
-
@proc ? @proc.call(@value) : @value
-
ensure
-
@value = @value.next
-
end
-
-
1
def names
-
[@name] + @aliases
-
end
-
end
-
end
-
1
module FactoryGirl
-
1
module Strategy
-
1
class AttributesFor
-
1
def association(runner)
-
runner.run(Strategy::Null)
-
end
-
-
1
def result(evaluation)
-
evaluation.hash
-
end
-
end
-
end
-
end
-
1
module FactoryGirl
-
1
module Strategy
-
1
class Build
-
1
def association(runner)
-
runner.run
-
end
-
-
1
def result(evaluation)
-
evaluation.object.tap do |instance|
-
evaluation.notify(:after_build, instance)
-
end
-
end
-
end
-
end
-
end
-
1
module FactoryGirl
-
1
module Strategy
-
1
class Create
-
1
def association(runner)
-
runner.run
-
end
-
-
1
def result(evaluation)
-
evaluation.object.tap do |instance|
-
evaluation.notify(:after_build, instance)
-
evaluation.notify(:before_create, instance)
-
evaluation.create(instance)
-
evaluation.notify(:after_create, instance)
-
end
-
end
-
end
-
end
-
end
-
1
module FactoryGirl
-
1
module Strategy
-
1
class Null
-
1
def association(runner)
-
end
-
-
1
def result(evaluation)
-
end
-
end
-
end
-
end
-
1
module FactoryGirl
-
1
module Strategy
-
1
class Stub
-
1
@@next_id = 1000
-
-
1
def association(runner)
-
runner.run(Strategy::Stub)
-
end
-
-
1
def result(evaluation)
-
evaluation.object.tap do |instance|
-
stub_database_interaction_on_result(instance)
-
evaluation.notify(:after_stub, instance)
-
end
-
end
-
-
1
private
-
-
1
def next_id
-
@@next_id += 1
-
end
-
-
1
def stub_database_interaction_on_result(result_instance)
-
result_instance.id = next_id
-
result_instance.instance_eval do
-
def persisted?
-
!new_record?
-
end
-
-
def created_at
-
@created_at ||= Time.now
-
end
-
-
def new_record?
-
id.nil?
-
end
-
-
def save(*args)
-
raise "stubbed models are not allowed to access the database"
-
end
-
-
def destroy(*args)
-
raise "stubbed models are not allowed to access the database"
-
end
-
-
def connection
-
raise "stubbed models are not allowed to access the database"
-
end
-
-
def reload
-
raise "stubbed models are not allowed to access the database"
-
end
-
-
def update_attribute(*args)
-
raise "stubbed models are not allowed to access the database"
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module FactoryGirl
-
1
class StrategyCalculator
-
1
def initialize(name_or_object)
-
@name_or_object = name_or_object
-
end
-
-
1
def strategy
-
if strategy_is_object?
-
@name_or_object
-
else
-
strategy_name_to_object
-
end
-
end
-
-
1
private
-
-
1
def strategy_is_object?
-
@name_or_object.is_a?(Class)
-
end
-
-
1
def strategy_name_to_object
-
case @name_or_object
-
when :build then Strategy::Build
-
when :create then Strategy::Create
-
else raise "unrecognized method #{@name_or_object}"
-
end
-
end
-
end
-
end
-
1
require "factory_girl/syntax/methods"
-
1
require "factory_girl/syntax/default"
-
1
require "factory_girl/syntax/vintage"
-
-
1
module FactoryGirl
-
# Provides alternate syntaxes for factory_girl. If you don't like the default
-
# syntax for defining or using factories, look at one of the
-
# FactoryGirl::Syntax modules:
-
#
-
# * FactoryGirl::Syntax::Blueprint: definition syntax similar to Machinist
-
# * FactoryGirl::Syntax::Generate: usage syntax similar to Object Daddy
-
# * FactoryGirl::Syntax::Make: usage syntax similar to Machinist
-
# * FactoryGirl::Syntax::Sham: sequence syntax similar to Machinist
-
1
module Syntax
-
end
-
end
-
1
module FactoryGirl
-
1
module Syntax
-
1
module Default
-
1
include Methods
-
-
1
def define(&block)
-
3
DSL.run(block)
-
end
-
-
1
def modify(&block)
-
ModifyDSL.run(block)
-
end
-
-
1
class DSL
-
1
def self.run(block)
-
3
new.instance_eval(&block)
-
end
-
-
1
def factory(name, options = {}, &block)
-
7
factory = Factory.new(name, options)
-
7
proxy = FactoryGirl::DefinitionProxy.new(factory.definition)
-
7
proxy.instance_eval(&block) if block_given?
-
-
7
FactoryGirl.register_factory(factory)
-
-
7
proxy.child_factories.each do |(child_name, child_options, child_block)|
-
parent_factory = child_options.delete(:parent) || name
-
factory(child_name, child_options.merge(parent: parent_factory), &child_block)
-
end
-
end
-
-
1
def sequence(name, *args, &block)
-
FactoryGirl.register_sequence(Sequence.new(name, *args, &block))
-
end
-
-
1
def trait(name, &block)
-
FactoryGirl.register_trait(Trait.new(name, &block))
-
end
-
end
-
-
1
class ModifyDSL
-
1
def self.run(block)
-
new.instance_eval(&block)
-
end
-
-
1
def factory(name, options = {}, &block)
-
factory = FactoryGirl.factory_by_name(name)
-
proxy = FactoryGirl::DefinitionProxy.new(factory.definition.overridable)
-
proxy.instance_eval(&block)
-
end
-
end
-
end
-
end
-
-
1
extend Syntax::Default
-
end
-
1
module FactoryGirl
-
1
module Syntax
-
1
module Methods
-
# Generates and returns a Hash of attributes from this factory. Attributes
-
# can be individually overridden by passing in a Hash of attribute => value
-
# pairs.
-
#
-
# Arguments:
-
# * name: +Symbol+ or +String+
-
# The name of the factory that should be used.
-
# * traits_and_overrides: +Array+
-
# [+*Array+] Traits to be applied
-
# [+Hash+] Attributes to overwrite for this set.
-
# * block:
-
# Yields the hash of attributes.
-
#
-
# Returns: +Hash+
-
# A set of attributes that can be used to build an instance of the class
-
# this factory generates.
-
1
def attributes_for(name, *traits_and_overrides, &block)
-
FactoryRunner.new(name, Strategy::AttributesFor, traits_and_overrides).run(&block)
-
end
-
-
# Generates and returns an instance from this factory. Attributes can be
-
# individually overridden by passing in a Hash of attribute => value pairs.
-
#
-
# Arguments:
-
# * name: +Symbol+ or +String+
-
# The name of the factory that should be used.
-
# * traits_and_overrides: +Array+
-
# [+*Array+] Traits to be applied
-
# [+Hash+] Attributes to overwrite for this instance.
-
# * block:
-
# Yields the built instance.
-
#
-
# Returns: +Object+
-
# An instance of the class this factory generates, with generated attributes
-
# assigned.
-
1
def build(name, *traits_and_overrides, &block)
-
FactoryRunner.new(name, Strategy::Build, traits_and_overrides).run(&block)
-
end
-
-
# Generates, saves, and returns an instance from this factory. Attributes can
-
# be individually overridden by passing in a Hash of attribute => value
-
# pairs.
-
#
-
# Instances are saved using the +save!+ method, so ActiveRecord models will
-
# raise ActiveRecord::RecordInvalid exceptions for invalid attribute sets.
-
#
-
# Arguments:
-
# * name: +Symbol+ or +String+
-
# The name of the factory that should be used.
-
# * traits_and_overrides: +Array+
-
# [+*Array+] Traits to be applied
-
# [+Hash+] Attributes to overwrite for this instance.
-
# * block:
-
# Yields the created instance.
-
#
-
# Returns: +Object+
-
# A saved instance of the class this factory generates, with generated
-
# attributes assigned.
-
1
def create(name, *traits_and_overrides, &block)
-
FactoryRunner.new(name, Strategy::Create, traits_and_overrides).run(&block)
-
end
-
-
# Generates and returns an object with all attributes from this factory
-
# stubbed out. Attributes can be individually overridden by passing in a Hash
-
# of attribute => value pairs.
-
#
-
# Arguments:
-
# * name: +Symbol+ or +String+
-
# The name of the factory that should be used.
-
# * traits_and_overrides: +Array+
-
# [+*Array+] Traits to be applied
-
# [+Hash+] Attributes to overwrite for this instance.
-
# * block
-
# Yields the stubbed object.
-
#
-
# Returns: +Object+
-
# An object with generated attributes stubbed out.
-
1
def build_stubbed(name, *traits_and_overrides, &block)
-
FactoryRunner.new(name, Strategy::Stub, traits_and_overrides).run(&block)
-
end
-
-
# Builds and returns multiple instances from this factory as an array. Attributes can be
-
# individually overridden by passing in a Hash of attribute => value pairs.
-
#
-
# Arguments:
-
# * name: +Symbol+ or +String+
-
# The name of the factory to be used.
-
# * amount: +Integer+
-
# number of instances to be built.
-
# * traits_and_overrides: +Array+
-
# [+*Array+] Traits to be applied
-
# [+Hash+] Attributes to overwrite for this instance.
-
#
-
# Returns: +Array+
-
# An array of instances of the class this factory generates, with generated attributes
-
# assigned.
-
1
def build_list(name, amount, *traits_and_overrides)
-
amount.times.map { build(name, *traits_and_overrides) }
-
end
-
-
# Creates and returns multiple instances from this factory as an array. Attributes can be
-
# individually overridden by passing in a Hash of attribute => value pairs.
-
#
-
# Arguments:
-
# * name: +Symbol+ or +String+
-
# The name of the factory to be used.
-
# * amount: +Integer+
-
# number of instances to be created.
-
# * traits_and_overrides: +Array+
-
# [+*Array+] Traits to be applied
-
# [+Hash+] Attributes to overwrite for this instance.
-
#
-
# Returns: +Array+
-
# An array of instances of the class this factory generates, with generated attributes
-
# assigned.
-
1
def create_list(name, amount, *traits_and_overrides)
-
amount.times.map { create(name, *traits_and_overrides) }
-
end
-
-
# Generates and returns the next value in a sequence.
-
#
-
# Arguments:
-
# name: (Symbol)
-
# The name of the sequence that a value should be generated for.
-
#
-
# Returns:
-
# The next value in the sequence. (Object)
-
1
def generate(name)
-
FactoryGirl.sequence_by_name(name).next
-
end
-
end
-
end
-
end
-
1
require "active_support/deprecation"
-
-
1
module FactoryGirl
-
1
module Syntax
-
1
module Vintage
-
1
module ::Factory
-
# Defines a new factory that can be used by the build strategies (create and
-
# build) to build new objects.
-
#
-
# Arguments:
-
# * name: +Symbol+ or +String+
-
# A unique name used to identify this factory.
-
# * options: +Hash+
-
#
-
# Options:
-
# * class: +Symbol+, +Class+, or +String+
-
# The class that will be used when generating instances for this factory. If not specified, the class will be guessed from the factory name.
-
# * parent: +Symbol+
-
# The parent factory. If specified, the attributes from the parent
-
# factory will be copied to the current one with an ability to override
-
# them.
-
#
-
# Yields: +Factory+
-
# The newly created factory.
-
1
def self.define(name, options = {})
-
ActiveSupport::Deprecation.warn "Factory.define is deprecated; use the FactoryGirl.define block syntax to declare your factory.", caller
-
factory = FactoryGirl::Factory.new(name, options)
-
proxy = FactoryGirl::DefinitionProxy.new(factory)
-
yield(proxy)
-
FactoryGirl.register_factory(factory)
-
end
-
-
# Defines a new sequence that can be used to generate unique values in a specific format.
-
#
-
# Arguments:
-
# name: (Symbol)
-
# A unique name for this sequence. This name will be referenced when
-
# calling next to generate new values from this sequence.
-
# block: (Proc)
-
# The code to generate each value in the sequence. This block will be
-
# called with a unique number each time a value in the sequence is to be
-
# generated. The block should return the generated value for the
-
# sequence.
-
#
-
# Example:
-
#
-
# Factory.sequence(:email) {|n| "somebody_#{n}@example.com" }
-
1
def self.sequence(name, start_value = 1, &block)
-
ActiveSupport::Deprecation.warn "Factory.sequence is deprecated; use the FactoryGirl.define block syntax to declare your sequence.", caller
-
FactoryGirl.register_sequence(Sequence.new(name, start_value, &block))
-
end
-
-
# Generates and returns the next value in a sequence.
-
#
-
# Arguments:
-
# name: (Symbol)
-
# The name of the sequence that a value should be generated for.
-
#
-
# Returns:
-
# The next value in the sequence. (Object)
-
1
def self.next(name)
-
ActiveSupport::Deprecation.warn "Factory.next is deprecated; use FactoryGirl.generate instead.", caller
-
FactoryGirl.generate(name)
-
end
-
-
# Defines a new alias for attributes.
-
#
-
# Arguments:
-
# * pattern: +Regexp+
-
# A pattern that will be matched against attributes when looking for
-
# aliases. Contents captured in the pattern can be used in the alias.
-
# * replace: +String+
-
# The alias that results from the matched pattern. Captured strings can
-
# be substituted like with +String#sub+.
-
#
-
# Example:
-
#
-
# Factory.alias /(.*)_confirmation/, '\1'
-
#
-
# factory_girl starts with aliases for foreign keys, so that a :user
-
# association can be overridden by a :user_id parameter:
-
#
-
# Factory.define :post do |p|
-
# p.association :user
-
# end
-
#
-
# # The user association will not be built in this example. The user_id
-
# # will be used instead.
-
# Factory(:post, user_id: 1)
-
1
def self.alias(pattern, replace)
-
ActiveSupport::Deprecation.warn "Factory.alias is deprecated; use FactoryGirl.aliases << [pattern, replace] instead.", caller
-
FactoryGirl.aliases << [pattern, replace]
-
end
-
-
# Alias for FactoryGirl.attributes_for
-
1
def self.attributes_for(name, overrides = {})
-
ActiveSupport::Deprecation.warn "Factory.attributes_for is deprecated; use FactoryGirl.attributes_for instead.", caller
-
FactoryGirl.attributes_for(name, overrides)
-
end
-
-
# Alias for FactoryGirl.build
-
1
def self.build(name, overrides = {})
-
ActiveSupport::Deprecation.warn "Factory.build is deprecated; use FactoryGirl.build instead.", caller
-
FactoryGirl.build(name, overrides)
-
end
-
-
# Alias for FactoryGirl.create
-
1
def self.create(name, overrides = {})
-
ActiveSupport::Deprecation.warn "Factory.create is deprecated; use FactoryGirl.create instead.", caller
-
FactoryGirl.create(name, overrides)
-
end
-
-
# Alias for FactoryGirl.build_stubbed.
-
1
def self.stub(name, overrides = {})
-
ActiveSupport::Deprecation.warn "Factory.stub is deprecated; use FactoryGirl.build_stubbed instead.", caller
-
FactoryGirl.build_stubbed(name, overrides)
-
end
-
end
-
-
# Shortcut for Factory.create.
-
#
-
# Example:
-
# Factory(:user, name: 'Joe')
-
1
def Factory(name, attrs = {})
-
ActiveSupport::Deprecation.warn "Factory(:name) is deprecated; use FactoryGirl.create(:name) instead.", caller
-
FactoryGirl.create(name, attrs)
-
end
-
end
-
end
-
end
-
-
1
include FactoryGirl::Syntax::Vintage
-
1
module FactoryGirl
-
1
class Trait
-
1
attr_reader :name
-
-
1
def initialize(name, &block) #:nodoc:
-
@name = name
-
@block = block
-
@definition = Definition.new
-
-
proxy = FactoryGirl::DefinitionProxy.new(@definition)
-
proxy.instance_eval(&@block) if block_given?
-
end
-
-
1
delegate :add_callback, :declare_attribute, :to_create, :define_trait,
-
:callbacks, :attributes, to: :@definition
-
-
1
def names
-
[@name]
-
end
-
-
1
def ==(other)
-
name == other.name &&
-
block == other.block
-
end
-
-
1
protected
-
1
attr_reader :block
-
end
-
end
-
1
module FactoryGirl
-
1
VERSION = "3.1.1"
-
end
-
-
1
require 'factory_girl_rails/railtie'
-
-
1
require 'factory_girl'
-
1
require 'rails'
-
-
1
module FactoryGirl
-
1
class Railtie < Rails::Railtie
-
-
1
initializer "factory_girl.set_fixture_replacement" do
-
1
generators = config.respond_to?(:app_generators) ? config.app_generators : config.generators
-
-
1
if generators.options[:rails][:test_framework] == :rspec
-
1
generators.fixture_replacement :factory_girl, :dir => 'spec/factories'
-
else
-
generators.test_framework :test_unit, :fixture => false, :fixture_replacement => :factory_girl
-
end
-
end
-
-
1
initializer "factory_girl.set_factory_paths" do
-
1
FactoryGirl.definition_file_paths = [
-
File.join(Rails.root, 'factories'),
-
File.join(Rails.root, 'test', 'factories'),
-
File.join(Rails.root, 'spec', 'factories')
-
]
-
end
-
-
1
config.after_initialize do
-
1
FactoryGirl.find_definitions
-
end
-
end
-
end
-
-
1
require 'rbconfig'
-
-
1
module Gherkin
-
1
module CLexer
-
1
def self.[](i18n_underscored_iso_code)
-
2
begin
-
2
prefix = RbConfig::CONFIG['arch'] =~ /mswin|mingw/ ? "#{RbConfig::CONFIG['MAJOR']}.#{RbConfig::CONFIG['MINOR']}/" : ''
-
2
lib = "#{prefix}gherkin_lexer_#{i18n_underscored_iso_code}"
-
2
require lib
-
2
const_get(i18n_underscored_iso_code.capitalize)
-
rescue LoadError => e
-
e.message << %{\nCouldn't load #{lib}\nThe $LOAD_PATH was:\n#{$LOAD_PATH.join("\n")}}
-
raise e
-
end
-
end
-
end
-
end
-
1
module Hike
-
1
VERSION = "1.2.0"
-
-
1
autoload :Extensions, "hike/extensions"
-
1
autoload :Index, "hike/index"
-
1
autoload :NormalizedArray, "hike/normalized_array"
-
1
autoload :Paths, "hike/paths"
-
1
autoload :Trail, "hike/trail"
-
end
-
1
require 'hike/normalized_array'
-
-
1
module Hike
-
# `Extensions` is an internal collection for tracking extension names.
-
1
class Extensions < NormalizedArray
-
# Extensions added to this array are normalized with a leading
-
# `.`.
-
#
-
# extensions << "js"
-
# extensions << ".css"
-
#
-
# extensions
-
# # => [".js", ".css"]
-
#
-
1
def normalize_element(extension)
-
11
if extension[/^\./]
-
11
extension
-
else
-
".#{extension}"
-
end
-
end
-
end
-
end
-
1
require 'pathname'
-
-
1
module Hike
-
# `Index` is an internal cached variant of `Trail`. It assumes the
-
# file system does not change between `find` calls. All `stat` and
-
# `entries` calls are cached for the lifetime of the `Index` object.
-
1
class Index
-
# `Index#paths` is an immutable `Paths` collection.
-
1
attr_reader :paths
-
-
# `Index#extensions` is an immutable `Extensions` collection.
-
1
attr_reader :extensions
-
-
# `Index#aliases` is an immutable `Hash` mapping an extension to
-
# an `Array` of aliases.
-
1
attr_reader :aliases
-
-
# `Index.new` is an internal method. Instead of constructing it
-
# directly, create a `Trail` and call `Trail#index`.
-
1
def initialize(root, paths, extensions, aliases)
-
@root = root
-
-
# Freeze is used here so an error is throw if a mutator method
-
# is called on the array. Mutating `@paths`, `@extensions`, or
-
# `@aliases` would have unpredictable results.
-
@paths = paths.dup.freeze
-
@extensions = extensions.dup.freeze
-
@aliases = aliases.inject({}) { |h, (k, a)|
-
h[k] = a.dup.freeze; h
-
}.freeze
-
@pathnames = paths.map { |path| Pathname.new(path) }
-
-
@stats = {}
-
@entries = {}
-
@patterns = {}
-
end
-
-
# `Index#root` returns root path as a `String`. This attribute is immutable.
-
1
def root
-
@root.to_s
-
end
-
-
# `Index#index` returns `self` to be compatable with the `Trail` interface.
-
1
def index
-
self
-
end
-
-
# The real implementation of `find`. `Trail#find` generates a one
-
# time index and delegates here.
-
#
-
# See `Trail#find` for usage.
-
1
def find(*logical_paths, &block)
-
if block_given?
-
options = extract_options!(logical_paths)
-
base_path = Pathname.new(options[:base_path] || @root)
-
-
logical_paths.each do |logical_path|
-
logical_path = Pathname.new(logical_path.sub(/^\//, ''))
-
-
if relative?(logical_path)
-
find_in_base_path(logical_path, base_path, &block)
-
else
-
find_in_paths(logical_path, &block)
-
end
-
end
-
-
nil
-
else
-
find(*logical_paths) do |path|
-
return path
-
end
-
end
-
end
-
-
# A cached version of `Dir.entries` that filters out `.` files and
-
# `~` swap files. Returns an empty `Array` if the directory does
-
# not exist.
-
1
def entries(path)
-
key = path.to_s
-
@entries[key] ||= Pathname.new(path).entries.reject { |entry| entry.to_s =~ /^\.|~$|^\#.*\#$/ }.sort
-
rescue Errno::ENOENT
-
@entries[key] = []
-
end
-
-
# A cached version of `File.stat`. Returns nil if the file does
-
# not exist.
-
1
def stat(path)
-
key = path.to_s
-
if @stats.key?(key)
-
@stats[key]
-
else
-
begin
-
@stats[key] = File.stat(path)
-
rescue Errno::ENOENT
-
@stats[key] = nil
-
end
-
end
-
end
-
-
1
protected
-
1
def extract_options!(arguments)
-
arguments.last.is_a?(Hash) ? arguments.pop.dup : {}
-
end
-
-
1
def relative?(logical_path)
-
logical_path.to_s =~ /^\.\.?\//
-
end
-
-
# Finds logical path across all `paths`
-
1
def find_in_paths(logical_path, &block)
-
dirname, basename = logical_path.split
-
@pathnames.each do |base_path|
-
match(base_path.join(dirname), basename, &block)
-
end
-
end
-
-
# Finds relative logical path, `../test/test_trail`. Requires a
-
# `base_path` for reference.
-
1
def find_in_base_path(logical_path, base_path, &block)
-
candidate = base_path.join(logical_path)
-
dirname, basename = candidate.split
-
match(dirname, basename, &block) if paths_contain?(dirname)
-
end
-
-
# Checks if the path is actually on the file system and performs
-
# any syscalls if necessary.
-
1
def match(dirname, basename)
-
# Potential `entries` syscall
-
matches = entries(dirname)
-
-
pattern = pattern_for(basename)
-
matches = matches.select { |m| m.to_s =~ pattern }
-
-
sort_matches(matches, basename).each do |path|
-
pathname = dirname.join(path)
-
-
# Potential `stat` syscall
-
stat = stat(pathname)
-
-
# Exclude directories
-
if stat && stat.file?
-
yield pathname.to_s
-
end
-
end
-
end
-
-
# Returns true if `dirname` is a subdirectory of any of the `paths`
-
1
def paths_contain?(dirname)
-
paths.any? { |path| dirname.to_s[0, path.length] == path }
-
end
-
-
# Cache results of `build_pattern_for`
-
1
def pattern_for(basename)
-
@patterns[basename] ||= build_pattern_for(basename)
-
end
-
-
# Returns a `Regexp` that matches the allowed extensions.
-
#
-
# pattern_for("index.html") #=> /^index(.html|.htm)(.builder|.erb)*$/
-
1
def build_pattern_for(basename)
-
extname = basename.extname
-
aliases = find_aliases_for(extname)
-
-
if aliases.any?
-
basename = basename.basename(extname)
-
aliases = [extname] + aliases
-
aliases_pattern = aliases.map { |e| Regexp.escape(e) }.join("|")
-
basename_re = Regexp.escape(basename.to_s) + "(?:#{aliases_pattern})"
-
else
-
basename_re = Regexp.escape(basename.to_s)
-
end
-
-
extension_pattern = extensions.map { |e| Regexp.escape(e) }.join("|")
-
/^#{basename_re}(?:#{extension_pattern})*$/
-
end
-
-
# Sorts candidate matches by their extension
-
# priority. Extensions in the front of the `extensions` carry
-
# more weight.
-
1
def sort_matches(matches, basename)
-
aliases = find_aliases_for(basename.extname)
-
-
matches.sort_by do |match|
-
extnames = match.sub(basename.to_s, '').to_s.scan(/\.[^.]+/)
-
extnames.inject(0) do |sum, ext|
-
if i = extensions.index(ext)
-
sum + i + 1
-
elsif i = aliases.index(ext)
-
sum + i + 11
-
else
-
sum
-
end
-
end
-
end
-
end
-
-
1
def find_aliases_for(extension)
-
@aliases.inject([]) do |aliases, (key, value)|
-
aliases.push(key) if value == extension
-
aliases
-
end
-
end
-
end
-
end
-
1
module Hike
-
# `NormalizedArray` is an internal abstract wrapper class that calls
-
# a callback `normalize_element` anytime an element is added to the
-
# Array.
-
#
-
# `Extensions` and `Paths` are subclasses of `NormalizedArray`.
-
1
class NormalizedArray < Array
-
1
def initialize
-
2
super()
-
end
-
-
1
def []=(*args)
-
value = args.pop
-
-
if value.respond_to?(:to_ary)
-
value = normalize_elements(value)
-
else
-
value = normalize_element(value)
-
end
-
-
super(*args.concat([value]))
-
end
-
-
1
def <<(element)
-
super normalize_element(element)
-
end
-
-
1
def collect!
-
super do |element|
-
result = yield element
-
normalize_element(result)
-
end
-
end
-
-
1
alias_method :map!, :collect!
-
-
1
def insert(index, *elements)
-
super index, *normalize_elements(elements)
-
end
-
-
1
def push(*elements)
-
16
super(*normalize_elements(elements))
-
end
-
-
1
def replace(elements)
-
super normalize_elements(elements)
-
end
-
-
1
def unshift(*elements)
-
super(*normalize_elements(elements))
-
end
-
-
1
def normalize_elements(elements)
-
16
elements.map do |element|
-
16
normalize_element(element)
-
end
-
end
-
end
-
end
-
1
require 'pathname'
-
1
require 'hike/normalized_array'
-
-
1
module Hike
-
# `Paths` is an internal collection for tracking path strings.
-
1
class Paths < NormalizedArray
-
1
def initialize(root = ".")
-
1
@root = Pathname.new(root)
-
1
super()
-
end
-
-
# Relative paths added to this array are expanded relative to `@root`.
-
#
-
# paths = Paths.new("/usr/local")
-
# paths << "tmp"
-
# paths << "/tmp"
-
#
-
# paths
-
# # => ["/usr/local/tmp", "/tmp"]
-
#
-
1
def normalize_element(path)
-
5
path = Pathname.new(path)
-
5
path = @root.join(path) if path.relative?
-
5
path.expand_path.to_s
-
end
-
end
-
end
-
1
require 'pathname'
-
1
require 'hike/extensions'
-
1
require 'hike/index'
-
1
require 'hike/paths'
-
-
1
module Hike
-
# `Trail` is the public container class for holding paths and extensions.
-
1
class Trail
-
# `Trail#paths` is a mutable `Paths` collection.
-
#
-
# trail = Hike::Trail.new
-
# trail.paths.push "~/Projects/hike/lib", "~/Projects/hike/test"
-
#
-
# The order of the paths is significant. Paths in the beginning of
-
# the collection will be checked first. In the example above,
-
# `~/Projects/hike/lib/hike.rb` would shadow the existent of
-
# `~/Projects/hike/test/hike.rb`.
-
1
attr_reader :paths
-
-
# `Trail#extensions` is a mutable `Extensions` collection.
-
#
-
# trail = Hike::Trail.new
-
# trail.paths.push "~/Projects/hike/lib"
-
# trail.extensions.push ".rb"
-
#
-
# Extensions allow you to find files by just their name omitting
-
# their extension. Is similar to Ruby's require mechanism that
-
# allows you to require files with specifiying `foo.rb`.
-
1
attr_reader :extensions
-
-
# `Index#aliases` is a mutable `Hash` mapping an extension to
-
# an `Array` of aliases.
-
#
-
# trail = Hike::Trail.new
-
# trail.paths.push "~/Projects/hike/site"
-
# trail.aliases['.htm'] = 'html'
-
# trail.aliases['.xhtml'] = 'html'
-
# trail.aliases['.php'] = 'html'
-
#
-
# Aliases provide a fallback when the primary extension is not
-
# matched. In the example above, a lookup for "foo.html" will
-
# check for the existence of "foo.htm", "foo.xhtml", or "foo.php".
-
1
attr_reader :aliases
-
-
# A Trail accepts an optional root path that defaults to your
-
# current working directory. Any relative paths added to
-
# `Trail#paths` will expanded relative to the root.
-
1
def initialize(root = ".")
-
1
@root = Pathname.new(root).expand_path
-
1
@paths = Paths.new(@root)
-
1
@extensions = Extensions.new
-
1
@aliases = Hash.new { |h, k| h[k] = Extensions.new }
-
end
-
-
# `Trail#root` returns root path as a `String`. This attribute is immutable.
-
1
def root
-
@root.to_s
-
end
-
-
# Prepend `path` to `Paths` collection
-
1
def prepend_paths(*paths)
-
self.paths.unshift(*paths)
-
end
-
1
alias_method :prepend_path, :prepend_paths
-
-
# Append `path` to `Paths` collection
-
1
def append_paths(*paths)
-
5
self.paths.push(*paths)
-
end
-
1
alias_method :append_path, :append_paths
-
-
# Remove `path` from `Paths` collection
-
1
def remove_path(path)
-
self.paths.delete(path)
-
end
-
-
# Prepend `extension` to `Extensions` collection
-
1
def prepend_extensions(*extensions)
-
self.extensions.unshift(*extensions)
-
end
-
1
alias_method :prepend_extension, :prepend_extensions
-
-
# Append `extension` to `Extensions` collection
-
1
def append_extensions(*extensions)
-
11
self.extensions.push(*extensions)
-
end
-
1
alias_method :append_extension, :append_extensions
-
-
# Remove `extension` from `Extensions` collection
-
1
def remove_extension(extension)
-
self.extensions.delete(extension)
-
end
-
-
# Alias `new_extension` to `old_extension`
-
1
def alias_extension(new_extension, old_extension)
-
5
aliases[normalize_extension(new_extension)] = normalize_extension(old_extension)
-
end
-
-
# Remove the alias for `extension`
-
1
def unalias_extension(extension)
-
aliases.delete(normalize_extension(extension))
-
end
-
-
# `Trail#find` returns a the expand path for a logical path in the
-
# path collection.
-
#
-
# trail = Hike::Trail.new "~/Projects/hike"
-
# trail.extensions.push ".rb"
-
# trail.paths.push "lib", "test"
-
#
-
# trail.find "hike/trail"
-
# # => "~/Projects/hike/lib/hike/trail.rb"
-
#
-
# trail.find "test_trail"
-
# # => "~/Projects/hike/test/test_trail.rb"
-
#
-
# `find` accepts multiple fallback logical paths that returns the
-
# first match.
-
#
-
# trail.find "hike", "hike/index"
-
#
-
# is equivalent to
-
#
-
# trail.find("hike") || trail.find("hike/index")
-
#
-
# Though `find` always returns the first match, it is possible
-
# to iterate over all shadowed matches and fallbacks by supplying
-
# a block.
-
#
-
# trail.find("hike", "hike/index") { |path| warn path }
-
#
-
# This allows you to filter your matches by any condition.
-
#
-
# trail.find("application") do |path|
-
# return path if mime_type_for(path) == "text/css"
-
# end
-
#
-
1
def find(*args, &block)
-
index.find(*args, &block)
-
end
-
-
# `Trail#index` returns an `Index` object that has the same
-
# interface as `Trail`. An `Index` is a cached `Trail` object that
-
# does not update when the file system changes. If you are
-
# confident that you are not making changes the paths you are
-
# searching, `index` will avoid excess system calls.
-
#
-
# index = trail.index
-
# index.find "hike/trail"
-
# index.find "test_trail"
-
#
-
1
def index
-
Index.new(root, paths, extensions, aliases)
-
end
-
-
# `Trail#entries` is equivalent to `Dir#entries`. It is not
-
# recommend to use this method for general purposes. It exists for
-
# parity with `Index#entries`.
-
1
def entries(*args)
-
index.entries(*args)
-
end
-
-
# `Trail#stat` is equivalent to `File#stat`. It is not
-
# recommend to use this method for general purposes. It exists for
-
# parity with `Index#stat`.
-
1
def stat(*args)
-
index.stat(*args)
-
end
-
-
1
private
-
1
def normalize_extension(extension)
-
10
if extension[/^\./]
-
10
extension
-
else
-
".#{extension}"
-
end
-
end
-
end
-
end
-
1
module I18n
-
1
module Backend
-
1
autoload :Base, 'i18n/backend/base'
-
1
autoload :InterpolationCompiler, 'i18n/backend/interpolation_compiler'
-
1
autoload :Cache, 'i18n/backend/cache'
-
1
autoload :Cascade, 'i18n/backend/cascade'
-
1
autoload :Chain, 'i18n/backend/chain'
-
1
autoload :Fallbacks, 'i18n/backend/fallbacks'
-
1
autoload :Flatten, 'i18n/backend/flatten'
-
1
autoload :Gettext, 'i18n/backend/gettext'
-
1
autoload :KeyValue, 'i18n/backend/key_value'
-
1
autoload :Memoize, 'i18n/backend/memoize'
-
1
autoload :Metadata, 'i18n/backend/metadata'
-
1
autoload :Pluralization, 'i18n/backend/pluralization'
-
1
autoload :Simple, 'i18n/backend/simple'
-
1
autoload :Transliterator, 'i18n/backend/transliterator'
-
end
-
end
-
1
require 'yaml'
-
1
require 'i18n/core_ext/hash'
-
1
require 'i18n/core_ext/kernel/surpress_warnings'
-
-
1
module I18n
-
1
module Backend
-
1
module Base
-
1
include I18n::Backend::Transliterator
-
-
# Accepts a list of paths to translation files. Loads translations from
-
# plain Ruby (*.rb) or YAML files (*.yml). See #load_rb and #load_yml
-
# for details.
-
1
def load_translations(*filenames)
-
1
filenames = I18n.load_path if filenames.empty?
-
7
filenames.flatten.each { |filename| load_file(filename) }
-
end
-
-
# This method receives a locale, a data hash and options for storing translations.
-
# Should be implemented
-
1
def store_translations(locale, data, options = {})
-
raise NotImplementedError
-
end
-
-
1
def translate(locale, key, options = {})
-
2
raise InvalidLocale.new(locale) unless locale
-
2
entry = key && lookup(locale, key, options[:scope], options)
-
-
2
if options.empty?
-
entry = resolve(locale, key, entry, options)
-
else
-
2
count, default = options.values_at(:count, :default)
-
2
values = options.except(*RESERVED_KEYS)
-
2
entry = entry.nil? && default ?
-
default(locale, key, default, options) : resolve(locale, key, entry, options)
-
end
-
-
2
throw(:exception, I18n::MissingTranslation.new(locale, key, options)) if entry.nil?
-
2
entry = entry.dup if entry.is_a?(String)
-
-
2
entry = pluralize(locale, entry, count) if count
-
2
entry = interpolate(locale, entry, values) if values
-
2
entry
-
end
-
-
# Acts the same as +strftime+, but uses a localized version of the
-
# format string. Takes a key from the date/time formats translations as
-
# a format argument (<em>e.g.</em>, <tt>:short</tt> in <tt>:'date.formats'</tt>).
-
1
def localize(locale, object, format = :default, options = {})
-
raise ArgumentError, "Object must be a Date, DateTime or Time object. #{object.inspect} given." unless object.respond_to?(:strftime)
-
-
if Symbol === format
-
key = format
-
type = object.respond_to?(:sec) ? 'time' : 'date'
-
options = options.merge(:raise => true, :object => object, :locale => locale)
-
format = I18n.t(:"#{type}.formats.#{key}", options)
-
end
-
-
# format = resolve(locale, object, format, options)
-
format = format.to_s.gsub(/%[aAbBp]/) do |match|
-
case match
-
when '%a' then I18n.t(:"date.abbr_day_names", :locale => locale, :format => format)[object.wday]
-
when '%A' then I18n.t(:"date.day_names", :locale => locale, :format => format)[object.wday]
-
when '%b' then I18n.t(:"date.abbr_month_names", :locale => locale, :format => format)[object.mon]
-
when '%B' then I18n.t(:"date.month_names", :locale => locale, :format => format)[object.mon]
-
when '%p' then I18n.t(:"time.#{object.hour < 12 ? :am : :pm}", :locale => locale, :format => format) if object.respond_to? :hour
-
end
-
end
-
-
object.strftime(format)
-
end
-
-
# Returns an array of locales for which translations are available
-
# ignoring the reserved translation meta data key :i18n.
-
1
def available_locales
-
raise NotImplementedError
-
end
-
-
1
def reload!
-
1
@skip_syntax_deprecation = false
-
end
-
-
1
protected
-
-
# The method which actually looks up for the translation in the store.
-
1
def lookup(locale, key, scope = [], options = {})
-
raise NotImplementedError
-
end
-
-
# Evaluates defaults.
-
# If given subject is an Array, it walks the array and returns the
-
# first translation that can be resolved. Otherwise it tries to resolve
-
# the translation directly.
-
1
def default(locale, object, subject, options = {})
-
4
options = options.dup.reject { |key, value| key == :default }
-
2
case subject
-
when Array
-
subject.each do |item|
-
result = resolve(locale, object, item, options) and return result
-
end and nil
-
else
-
2
resolve(locale, object, subject, options)
-
end
-
end
-
-
# Resolves a translation.
-
# If the given subject is a Symbol, it will be translated with the
-
# given options. If it is a Proc then it will be evaluated. All other
-
# subjects will be returned directly.
-
1
def resolve(locale, object, subject, options = {})
-
2
return subject if options[:resolve] == false
-
2
result = catch(:exception) do
-
2
case subject
-
when Symbol
-
I18n.translate(subject, options.merge(:locale => locale, :throw => true))
-
when Proc
-
date_or_time = options.delete(:object) || object
-
resolve(locale, object, subject.call(date_or_time, options))
-
else
-
2
subject
-
end
-
end
-
2
result unless result.is_a?(MissingTranslation)
-
end
-
-
# Picks a translation from an array according to English pluralization
-
# rules. It will pick the first translation if count is not equal to 1
-
# and the second translation if it is equal to 1. Other backends can
-
# implement more flexible or complex pluralization rules.
-
1
def pluralize(locale, entry, count)
-
return entry unless entry.is_a?(Hash) && count
-
-
key = :zero if count == 0 && entry.has_key?(:zero)
-
key ||= count == 1 ? :one : :other
-
raise InvalidPluralizationData.new(entry, count) unless entry.has_key?(key)
-
entry[key]
-
end
-
-
# Interpolates values into a given string.
-
#
-
# interpolate "file %{file} opened by %%{user}", :file => 'test.txt', :user => 'Mr. X'
-
# # => "file test.txt opened by %{user}"
-
1
def interpolate(locale, string, values = {})
-
2
if string.is_a?(::String) && !values.empty?
-
I18n.interpolate(string, values)
-
else
-
2
string
-
end
-
end
-
-
# Loads a single translations file by delegating to #load_rb or
-
# #load_yml depending on the file extension and directly merges the
-
# data to the existing translations. Raises I18n::UnknownFileType
-
# for all other file extensions.
-
1
def load_file(filename)
-
6
type = File.extname(filename).tr('.', '').downcase
-
6
raise UnknownFileType.new(type, filename) unless respond_to?(:"load_#{type}", true)
-
6
data = send(:"load_#{type}", filename)
-
6
raise InvalidLocaleData.new(filename) unless data.is_a?(Hash)
-
12
data.each { |locale, d| store_translations(locale, d || {}) }
-
end
-
-
# Loads a plain Ruby translations file. eval'ing the file must yield
-
# a Hash containing translation data with locales as toplevel keys.
-
1
def load_rb(filename)
-
eval(IO.read(filename), binding, filename)
-
end
-
-
# Loads a YAML translations file. The data must have locales as
-
# toplevel keys.
-
1
def load_yml(filename)
-
6
YAML.load_file(filename)
-
end
-
end
-
end
-
end
-
1
module I18n
-
1
module Backend
-
# A simple backend that reads translations from YAML files and stores them in
-
# an in-memory hash. Relies on the Base backend.
-
#
-
# The implementation is provided by a Implementation module allowing to easily
-
# extend Simple backend's behavior by including modules. E.g.:
-
#
-
# module I18n::Backend::Pluralization
-
# def pluralize(*args)
-
# # extended pluralization logic
-
# super
-
# end
-
# end
-
#
-
# I18n::Backend::Simple.include(I18n::Backend::Pluralization)
-
1
class Simple
-
3
(class << self; self; end).class_eval { public :include }
-
-
1
module Implementation
-
1
include Base
-
-
1
def initialized?
-
2
@initialized ||= false
-
end
-
-
# Stores translations for the given locale in memory.
-
# This uses a deep merge for the translations hash, so existing
-
# translations will be overwritten by new ones only at the deepest
-
# level of the hash.
-
1
def store_translations(locale, data, options = {})
-
6
locale = locale.to_sym
-
6
translations[locale] ||= {}
-
6
data = data.deep_symbolize_keys
-
6
translations[locale].deep_merge!(data)
-
end
-
-
# Get available locales from the translations hash
-
1
def available_locales
-
init_translations unless initialized?
-
translations.inject([]) do |locales, (locale, data)|
-
locales << locale unless (data.keys - [:i18n]).empty?
-
locales
-
end
-
end
-
-
# Clean up translations hash and set initialized to false on reload!
-
1
def reload!
-
1
@initialized = false
-
1
@translations = nil
-
1
super
-
end
-
-
1
protected
-
-
1
def init_translations
-
1
load_translations
-
1
@initialized = true
-
end
-
-
1
def translations
-
14
@translations ||= {}
-
end
-
-
# Looks up a translation from the translations hash. Returns nil if
-
# eiher key is nil, or locale, scope or key do not exist as a key in the
-
# nested translations hash. Splits keys or scopes containing dots
-
# into multiple keys, i.e. <tt>currency.format</tt> is regarded the same as
-
# <tt>%w(currency format)</tt>.
-
1
def lookup(locale, key, scope = [], options = {})
-
2
init_translations unless initialized?
-
2
keys = I18n.normalize_keys(locale, key, scope, options[:separator])
-
-
2
keys.inject(translations) do |result, _key|
-
6
_key = _key.to_sym
-
6
return nil unless result.is_a?(Hash) && result.has_key?(_key)
-
4
result = result[_key]
-
4
result = resolve(locale, _key, result, options.merge(:scope => nil)) if result.is_a?(Symbol)
-
4
result
-
end
-
end
-
end
-
-
1
include Implementation
-
end
-
end
-
end
-
# encoding: utf-8
-
1
module I18n
-
1
module Backend
-
1
module Transliterator
-
1
DEFAULT_REPLACEMENT_CHAR = "?"
-
-
# Given a locale and a UTF-8 string, return the locale's ASCII
-
# approximation for the string.
-
1
def transliterate(locale, string, replacement = nil)
-
@transliterators ||= {}
-
@transliterators[locale] ||= Transliterator.get I18n.t(:'i18n.transliterate.rule',
-
:locale => locale, :resolve => false, :default => {})
-
@transliterators[locale].transliterate(string, replacement)
-
end
-
-
# Get a transliterator instance.
-
1
def self.get(rule = nil)
-
if !rule || rule.kind_of?(Hash)
-
HashTransliterator.new(rule)
-
elsif rule.kind_of? Proc
-
ProcTransliterator.new(rule)
-
else
-
raise I18n::ArgumentError, "Transliteration rule must be a proc or a hash."
-
end
-
end
-
-
# A transliterator which accepts a Proc as its transliteration rule.
-
1
class ProcTransliterator
-
1
def initialize(rule)
-
@rule = rule
-
end
-
-
1
def transliterate(string, replacement = nil)
-
@rule.call(string)
-
end
-
end
-
-
# A transliterator which accepts a Hash of characters as its translation
-
# rule.
-
1
class HashTransliterator
-
1
DEFAULT_APPROXIMATIONS = {
-
"À"=>"A", "Á"=>"A", "Â"=>"A", "Ã"=>"A", "Ä"=>"A", "Å"=>"A", "Æ"=>"AE",
-
"Ç"=>"C", "È"=>"E", "É"=>"E", "Ê"=>"E", "Ë"=>"E", "Ì"=>"I", "Í"=>"I",
-
"Î"=>"I", "Ï"=>"I", "Ð"=>"D", "Ñ"=>"N", "Ò"=>"O", "Ó"=>"O", "Ô"=>"O",
-
"Õ"=>"O", "Ö"=>"O", "×"=>"x", "Ø"=>"O", "Ù"=>"U", "Ú"=>"U", "Û"=>"U",
-
"Ü"=>"U", "Ý"=>"Y", "Þ"=>"Th", "ß"=>"ss", "à"=>"a", "á"=>"a", "â"=>"a",
-
"ã"=>"a", "ä"=>"a", "å"=>"a", "æ"=>"ae", "ç"=>"c", "è"=>"e", "é"=>"e",
-
"ê"=>"e", "ë"=>"e", "ì"=>"i", "í"=>"i", "î"=>"i", "ï"=>"i", "ð"=>"d",
-
"ñ"=>"n", "ò"=>"o", "ó"=>"o", "ô"=>"o", "õ"=>"o", "ö"=>"o", "ø"=>"o",
-
"ù"=>"u", "ú"=>"u", "û"=>"u", "ü"=>"u", "ý"=>"y", "þ"=>"th", "ÿ"=>"y",
-
"Ā"=>"A", "ā"=>"a", "Ă"=>"A", "ă"=>"a", "Ą"=>"A", "ą"=>"a", "Ć"=>"C",
-
"ć"=>"c", "Ĉ"=>"C", "ĉ"=>"c", "Ċ"=>"C", "ċ"=>"c", "Č"=>"C", "č"=>"c",
-
"Ď"=>"D", "ď"=>"d", "Đ"=>"D", "đ"=>"d", "Ē"=>"E", "ē"=>"e", "Ĕ"=>"E",
-
"ĕ"=>"e", "Ė"=>"E", "ė"=>"e", "Ę"=>"E", "ę"=>"e", "Ě"=>"E", "ě"=>"e",
-
"Ĝ"=>"G", "ĝ"=>"g", "Ğ"=>"G", "ğ"=>"g", "Ġ"=>"G", "ġ"=>"g", "Ģ"=>"G",
-
"ģ"=>"g", "Ĥ"=>"H", "ĥ"=>"h", "Ħ"=>"H", "ħ"=>"h", "Ĩ"=>"I", "ĩ"=>"i",
-
"Ī"=>"I", "ī"=>"i", "Ĭ"=>"I", "ĭ"=>"i", "Į"=>"I", "į"=>"i", "İ"=>"I",
-
"ı"=>"i", "IJ"=>"IJ", "ij"=>"ij", "Ĵ"=>"J", "ĵ"=>"j", "Ķ"=>"K", "ķ"=>"k",
-
"ĸ"=>"k", "Ĺ"=>"L", "ĺ"=>"l", "Ļ"=>"L", "ļ"=>"l", "Ľ"=>"L", "ľ"=>"l",
-
"Ŀ"=>"L", "ŀ"=>"l", "Ł"=>"L", "ł"=>"l", "Ń"=>"N", "ń"=>"n", "Ņ"=>"N",
-
"ņ"=>"n", "Ň"=>"N", "ň"=>"n", "ʼn"=>"'n", "Ŋ"=>"NG", "ŋ"=>"ng",
-
"Ō"=>"O", "ō"=>"o", "Ŏ"=>"O", "ŏ"=>"o", "Ő"=>"O", "ő"=>"o", "Œ"=>"OE",
-
"œ"=>"oe", "Ŕ"=>"R", "ŕ"=>"r", "Ŗ"=>"R", "ŗ"=>"r", "Ř"=>"R", "ř"=>"r",
-
"Ś"=>"S", "ś"=>"s", "Ŝ"=>"S", "ŝ"=>"s", "Ş"=>"S", "ş"=>"s", "Š"=>"S",
-
"š"=>"s", "Ţ"=>"T", "ţ"=>"t", "Ť"=>"T", "ť"=>"t", "Ŧ"=>"T", "ŧ"=>"t",
-
"Ũ"=>"U", "ũ"=>"u", "Ū"=>"U", "ū"=>"u", "Ŭ"=>"U", "ŭ"=>"u", "Ů"=>"U",
-
"ů"=>"u", "Ű"=>"U", "ű"=>"u", "Ų"=>"U", "ų"=>"u", "Ŵ"=>"W", "ŵ"=>"w",
-
"Ŷ"=>"Y", "ŷ"=>"y", "Ÿ"=>"Y", "Ź"=>"Z", "ź"=>"z", "Ż"=>"Z", "ż"=>"z",
-
"Ž"=>"Z", "ž"=>"z"
-
}
-
-
1
def initialize(rule = nil)
-
@rule = rule
-
add DEFAULT_APPROXIMATIONS
-
add rule if rule
-
end
-
-
1
def transliterate(string, replacement = nil)
-
string.gsub(/[^\x00-\x7f]/u) do |char|
-
approximations[char] || replacement || DEFAULT_REPLACEMENT_CHAR
-
end
-
end
-
-
1
private
-
-
1
def approximations
-
@approximations ||= {}
-
end
-
-
# Add transliteration rules to the approximations hash.
-
1
def add(hash)
-
hash.keys.each {|key| hash[key.to_s] = hash.delete(key).to_s}
-
approximations.merge! hash
-
end
-
end
-
end
-
end
-
end
-
1
class Hash
-
def slice(*keep_keys)
-
h = {}
-
keep_keys.each { |key| h[key] = fetch(key) }
-
h
-
1
end unless Hash.method_defined?(:slice)
-
-
def except(*less_keys)
-
slice(*keys - less_keys)
-
1
end unless Hash.method_defined?(:except)
-
-
def deep_symbolize_keys
-
53
inject({}) { |result, (key, value)|
-
162
value = value.deep_symbolize_keys if value.is_a?(Hash)
-
162
result[(key.to_sym rescue key) || key] = value
-
162
result
-
}
-
1
end unless Hash.method_defined?(:deep_symbolize_keys)
-
-
# deep_merge_hash! by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
-
1
MERGER = proc do |key, v1, v2|
-
Hash === v1 && Hash === v2 ? v1.merge(v2, &MERGER) : v2
-
end
-
-
def deep_merge!(data)
-
merge!(data, &MERGER)
-
1
end unless Hash.method_defined?(:deep_merge!)
-
end
-
-
1
module Kernel
-
1
def suppress_warnings
-
original_verbosity = $VERBOSE
-
$VERBOSE = nil
-
result = yield
-
$VERBOSE = original_verbosity
-
result
-
end
-
end
-
# This backports the Ruby 1.9 String interpolation syntax to Ruby 1.8.
-
#
-
# This backport has been shipped with I18n for a number of versions. Meanwhile
-
# Rails has started to rely on it and we are going to move it to ActiveSupport.
-
# See https://rails.lighthouseapp.com/projects/8994/tickets/6013-move-19-string-interpolation-syntax-backport-from-i18n-to-activesupport
-
#
-
# Once the above patch has been applied to Rails the following code will be
-
# removed from I18n.
-
-
=begin
-
heavily based on Masao Mutoh's gettext String interpolation extension
-
http://github.com/mutoh/gettext/blob/f6566738b981fe0952548c421042ad1e0cdfb31e/lib/gettext/core_ext/string.rb
-
Copyright (C) 2005-2009 Masao Mutoh
-
You may redistribute it and/or modify it under the same license terms as Ruby.
-
=end
-
-
1
begin
-
1
raise ArgumentError if ("a %{x}" % {:x=>'b'}) != 'a b'
-
rescue ArgumentError
-
# KeyError is raised by String#% when the string contains a named placeholder
-
# that is not contained in the given arguments hash. Ruby 1.9 includes and
-
# raises this exception natively. We define it to mimic Ruby 1.9's behaviour
-
# in Ruby 1.8.x
-
class KeyError < IndexError
-
def initialize(message = nil)
-
super(message || "key not found")
-
end
-
end unless defined?(KeyError)
-
-
# Extension for String class. This feature is included in Ruby 1.9 or later but not occur TypeError.
-
#
-
# String#% method which accept "named argument". The translator can know
-
# the meaning of the msgids using "named argument" instead of %s/%d style.
-
class String
-
# For older ruby versions, such as ruby-1.8.5
-
alias :bytesize :size unless instance_methods.find {|m| m.to_s == 'bytesize'}
-
alias :interpolate_without_ruby_19_syntax :% # :nodoc:
-
-
INTERPOLATION_PATTERN = Regexp.union(
-
/%\{(\w+)\}/, # matches placeholders like "%{foo}"
-
/%<(\w+)>(.*?\d*\.?\d*[bBdiouxXeEfgGcps])/ # matches placeholders like "%<foo>.d"
-
)
-
-
INTERPOLATION_PATTERN_WITH_ESCAPE = Regexp.union(
-
/%%/,
-
INTERPOLATION_PATTERN
-
)
-
-
# % uses self (i.e. the String) as a format specification and returns the
-
# result of applying it to the given arguments. In other words it interpolates
-
# the given arguments to the string according to the formats the string
-
# defines.
-
#
-
# There are three ways to use it:
-
#
-
# * Using a single argument or Array of arguments.
-
#
-
# This is the default behaviour of the String class. See Kernel#sprintf for
-
# more details about the format string.
-
#
-
# Example:
-
#
-
# "%d %s" % [1, "message"]
-
# # => "1 message"
-
#
-
# * Using a Hash as an argument and unformatted, named placeholders.
-
#
-
# When you pass a Hash as an argument and specify placeholders with %{foo}
-
# it will interpret the hash values as named arguments.
-
#
-
# Example:
-
#
-
# "%{firstname}, %{lastname}" % {:firstname => "Masao", :lastname => "Mutoh"}
-
# # => "Masao Mutoh"
-
#
-
# * Using a Hash as an argument and formatted, named placeholders.
-
#
-
# When you pass a Hash as an argument and specify placeholders with %<foo>d
-
# it will interpret the hash values as named arguments and format the value
-
# according to the formatting instruction appended to the closing >.
-
#
-
# Example:
-
#
-
# "%<integer>d, %<float>.1f" % { :integer => 10, :float => 43.4 }
-
# # => "10, 43.3"
-
def %(args)
-
if args.kind_of?(Hash)
-
dup.gsub(INTERPOLATION_PATTERN_WITH_ESCAPE) do |match|
-
if match == '%%'
-
'%'
-
else
-
key = ($1 || $2).to_sym
-
raise KeyError unless args.has_key?(key)
-
$3 ? sprintf("%#{$3}", args[key]) : args[key]
-
end
-
end
-
elsif self =~ INTERPOLATION_PATTERN
-
raise ArgumentError.new('one hash required')
-
else
-
result = gsub(/%([{<])/, '%%\1')
-
result.send :'interpolate_without_ruby_19_syntax', args
-
end
-
end
-
end
-
end
-
1
require 'jquery/rails'
-
1
module ActionDispatch
-
1
module Assertions
-
1
module SelectorAssertions
-
# Selects content from a JQuery response. Patterned loosely on
-
# assert_select_rjs.
-
#
-
# === Narrowing down
-
#
-
# With no arguments, asserts that one or more method calls are made.
-
#
-
# Use the +method+ argument to narrow down the assertion to only
-
# statements that call that specific method.
-
#
-
# Use the +opt+ argument to narrow down the assertion to only statements
-
# that pass +opt+ as the first argument.
-
#
-
# Use the +id+ argument to narrow down the assertion to only statements
-
# that invoke methods on the result of using that identifier as a
-
# selector.
-
#
-
# === Using blocks
-
#
-
# Without a block, +assert_select_jquery_ merely asserts that the
-
# response contains one or more statements that match the conditions
-
# specified above
-
#
-
# With a block +assert_select_jquery_ also asserts that the method call
-
# passes a javascript escaped string containing HTML. All such HTML
-
# fragments are selected and passed to the block. Nested assertions are
-
# supported.
-
#
-
# === Examples
-
#
-
# # asserts that the #notice element is hidden
-
# assert_select :hide, '#notice'
-
#
-
# # asserts that the #cart element is shown with a blind parameter
-
# assert_select :show, :blind, '#cart'
-
#
-
# # asserts that #cart content contains a #current_item
-
# assert_select :html, '#cart' do
-
# assert_select '#current_item'
-
# end
-
-
1
PATTERN_HTML = "\"((\\\\\"|[^\"])*)\""
-
1
PATTERN_UNICODE_ESCAPED_CHAR = /\\u([0-9a-zA-Z]{4})/
-
-
1
def assert_select_jquery(*args, &block)
-
jquery_method = args.first.is_a?(Symbol) ? args.shift : nil
-
jquery_opt = args.first.is_a?(Symbol) ? args.shift : nil
-
id = args.first.is_a?(String) ? args.shift : nil
-
-
pattern = "\\.#{jquery_method || '\\w+'}\\("
-
pattern = "#{pattern}['\"]#{jquery_opt}['\"],?\\s*" if jquery_opt
-
pattern = "#{pattern}#{PATTERN_HTML}"
-
pattern = "(?:jQuery|\\$)\\(['\"]#{id}['\"]\\)#{pattern}" if id
-
-
fragments = []
-
response.body.scan(Regexp.new(pattern)).each do |match|
-
doc = HTML::Document.new(unescape_js(match.first))
-
doc.root.children.each do |child|
-
fragments.push child if child.tag?
-
end
-
end
-
-
if fragments.empty?
-
opts = [jquery_method, jquery_opt, id].compact
-
flunk "No JQuery call matches #{opts.inspect}"
-
end
-
-
if block
-
begin
-
in_scope, @selected = @selected, fragments
-
yield
-
ensure
-
@selected = in_scope
-
end
-
end
-
end
-
-
1
private
-
-
# Unescapes a JS string.
-
1
def unescape_js(js_string)
-
# js encodes double quotes and line breaks.
-
unescaped= js_string.gsub('\"', '"')
-
unescaped.gsub!('\\\'', "'")
-
unescaped.gsub!(/\\\//, '/')
-
unescaped.gsub!('\n', "\n")
-
unescaped.gsub!('\076', '>')
-
unescaped.gsub!('\074', '<')
-
# js encodes non-ascii characters.
-
unescaped.gsub!(PATTERN_UNICODE_ESCAPED_CHAR) {|u| [$1.hex].pack('U*')}
-
unescaped
-
end
-
-
end
-
end
-
end
-
1
module Jquery
-
1
module Rails
-
1
PROTOTYPE_JS = %w{prototype effects dragdrop controls}
-
-
1
if ::Rails.version < "3.1"
-
require 'jquery/rails/railtie'
-
else
-
1
require 'jquery/rails/engine'
-
end
-
1
require 'jquery/rails/version'
-
end
-
end
-
# Configure Rails 3.1 to have assert_select_jquery() in tests
-
1
module Jquery
-
1
module Rails
-
-
1
class Engine < ::Rails::Engine
-
1
config.before_configuration do
-
1
require "jquery/assert_select" if ::Rails.env.test?
-
end
-
end
-
-
end
-
end
-
1
module Jquery
-
1
module Rails
-
1
VERSION = "1.0.19"
-
1
JQUERY_VERSION = "1.7.1"
-
1
JQUERY_UI_VERSION = "1.8.16"
-
1
JQUERY_UJS_VERSION = "82292010fb1743f038ab72b1f1e994e91be3eda3"
-
end
-
end
-
1
require 'addressable/uri'
-
-
#
-
# The entry point into Launchy. This is the sole supported public API.
-
#
-
# Launchy.open( uri, options = {} )
-
#
-
# The currently defined global options are:
-
#
-
# :debug Turn on debugging output
-
# :application Explicitly state what application class is going to be used
-
# :host_os Explicitly state what host operating system to pretend to be
-
# :ruby_engine Explicitly state what ruby engine to pretend to be under
-
# :dry_run Do nothing and print the command that would be executed on $stdout
-
#
-
# Other options may be used, and those will be passed directly to the
-
# application class
-
#
-
1
module Launchy
-
-
1
class << self
-
#
-
# Convenience method to launch an item
-
#
-
1
def open(uri, options = {} )
-
begin
-
extract_global_options( options )
-
uri = Addressable::URI.parse( uri )
-
app = Launchy::Application.handling( uri )
-
app.new.open( uri, options )
-
rescue Exception => e
-
msg = "Failure in opening #{uri} with options #{options.inspect}: #{e}"
-
Launchy.log "#{self.name} : #{msg}"
-
e.backtrace.each do |bt|
-
Launchy.log bt
-
end
-
$stderr.puts msg
-
end
-
end
-
-
1
def reset_global_options
-
Launchy.debug = false
-
Launchy.application = nil
-
Launchy.host_os = nil
-
Launchy.ruby_engine = nil
-
Launchy.dry_run = false
-
end
-
-
1
def extract_global_options( options )
-
Launchy.debug = options.delete( :debug ) || ENV['LAUNCHY_DEBUG']
-
Launchy.application = options.delete( :application ) || ENV['LAUNCHY_APPLICATION']
-
Launchy.host_os = options.delete( :host_os ) || ENV['LAUNCHY_HOST_OS']
-
Launchy.ruby_engine = options.delete( :ruby_engine ) || ENV['LAUNCHY_RUBY_ENGINE']
-
Launchy.dry_run = options.delete( :dry_run ) || ENV['LAUNCHY_DRY_RUN']
-
end
-
-
1
def debug=( d )
-
@debug = (d == "true")
-
end
-
-
# we may do logging before a call to 'open', hence the need to check
-
# LAUNCHY_DEBUG here
-
1
def debug?
-
@debug || (ENV['LAUNCHY_DEBUG'] == 'true')
-
end
-
-
1
def application=( app )
-
@application = app
-
end
-
-
1
def application
-
@application || ENV['LAUNCHY_APPLICATION']
-
end
-
-
1
def host_os=( host_os )
-
@host_os = host_os
-
end
-
-
1
def host_os
-
@host_os || ENV['LAUNCHY_HOST_OS']
-
end
-
-
1
def ruby_engine=( ruby_engine )
-
@ruby_engine = ruby_engine
-
end
-
-
1
def ruby_engine
-
@ruby_engine || ENV['LAUNCHY_RUBY_ENGINE']
-
end
-
-
1
def dry_run=( dry_run )
-
@dry_run = dry_run
-
end
-
-
1
def dry_run?
-
@dry_run
-
end
-
-
1
def bug_report_message
-
"Please rerun with environment variable LAUNCHY_DEBUG=true or the '-d' commandline option and file a bug at https://github.com/copiousfreetime/launchy/issues/new"
-
end
-
-
1
def log(msg)
-
$stderr.puts "LAUNCHY_DEBUG: #{msg}" if Launchy.debug?
-
end
-
end
-
end
-
-
1
require 'launchy/version'
-
1
require 'launchy/cli'
-
1
require 'launchy/descendant_tracker'
-
1
require 'launchy/error'
-
1
require 'launchy/application'
-
1
require 'launchy/detect'
-
1
require 'launchy/deprecated'
-
1
require 'set'
-
1
module Launchy
-
#
-
# Application is the base class of all the application types that launchy may
-
# invoke. It essentially defines the public api of the launchy system.
-
#
-
# Every class that inherits from Application must define:
-
#
-
# 1. A constructor taking no parameters
-
# 2. An instance method 'open' taking a string or URI as the first parameter and a
-
# hash as the second
-
# 3. A class method 'handles?' that takes a String and returns true if that
-
# class can handle the input.
-
1
class Application
-
1
extend DescendantTracker
-
-
1
class << self
-
# Find the application that handles the given uri.
-
#
-
# returns the Class that can handle the uri
-
1
def handling( uri )
-
klass = find_child( :handles?, uri )
-
return klass if klass
-
raise ApplicationNotFoundError, "No application found to handle '#{uri}'"
-
end
-
-
#
-
# Find the given executable in the available paths
-
1
def find_executable( bin, *paths )
-
paths = ENV['PATH'].split( File::PATH_SEPARATOR ) if paths.empty?
-
paths.each do |path|
-
file = File.join( path, bin )
-
if File.executable?( file ) then
-
Launchy.log "#{self.name} : found executable #{file}"
-
return file
-
end
-
end
-
Launchy.log "#{self.name} : Unable to find `#{bin}' in #{paths.join(", ")}"
-
return nil
-
end
-
end
-
-
1
attr_reader :host_os_family
-
1
attr_reader :ruby_engine
-
1
attr_reader :runner
-
-
1
def initialize
-
@host_os_family = Launchy::Detect::HostOsFamily.detect
-
@ruby_engine = Launchy::Detect::RubyEngine.detect
-
@runner = Launchy::Detect::Runner.detect
-
end
-
-
1
def find_executable( bin, *paths )
-
Application.find_executable( bin, *paths )
-
end
-
-
1
def run( cmd, *args )
-
runner.run( cmd, *args )
-
end
-
end
-
end
-
1
require 'launchy/applications/browser'
-
1
class Launchy::Application
-
#
-
# The class handling the browser application and all of its schemes
-
#
-
1
class Browser < Launchy::Application
-
1
def self.schemes
-
%w[ http https ftp file ]
-
end
-
-
1
def self.handles?( uri )
-
return true if schemes.include?( uri.scheme )
-
return true if File.exist?( uri.path )
-
end
-
-
1
def windows_app_list
-
[ 'start /b' ]
-
end
-
-
1
def cygwin_app_list
-
[ 'cmd /C start /b' ]
-
end
-
-
1
def darwin_app_list
-
[ find_executable( "open" ) ]
-
end
-
-
1
def nix_app_list
-
app_list = %w[ xdg-open ]
-
if nix_de = Launchy::Detect::NixDesktopEnvironment.detect then
-
app_list << nix_de.browser
-
app_list << nix_de.fallback_browsers
-
end
-
app_list.flatten!
-
app_list.delete_if { |b| b.nil? || (b.strip.size == 0) }
-
app_list.collect { |bin| find_executable( bin ) }.find_all { |x| not x.nil? }
-
end
-
-
# use a call back mechanism to get the right app_list that is decided by the
-
# host_os_family class.
-
1
def app_list
-
host_os_family.app_list( self )
-
end
-
-
1
def browser_env
-
return [] unless ENV['BROWSER']
-
browser_env = ENV['BROWSER'].split( File::PATH_SEPARATOR )
-
browser_env.flatten!
-
browser_env.delete_if { |b| b.nil? || (b.strip.size == 0) }
-
return browser_env
-
end
-
-
# Get the full commandline of what we are going to add the uri to
-
1
def browser_cmdline
-
possibilities = (browser_env + app_list).flatten
-
possibilities.each do |p|
-
Launchy.log "#{self.class.name} : possibility : #{p}"
-
end
-
if browser = possibilities.shift then
-
Launchy.log "#{self.class.name} : Using browser value '#{browser}'"
-
return browser
-
end
-
raise Launchy::CommandNotFoundError, "Unable to find a browser command. If this is unexpected, #{Launchy.bug_report_message}"
-
end
-
-
1
def cmd_and_args( uri, options = {} )
-
cmd = browser_cmdline
-
args = [ uri.to_s ]
-
if cmd =~ /%s/ then
-
cmd.gsub!( /%s/, args.shift )
-
end
-
return [cmd, args]
-
end
-
-
# final assembly of the command and do %s substitution
-
# http://www.catb.org/~esr/BROWSER/index.html
-
1
def open( uri, options = {} )
-
cmd, args = cmd_and_args( uri, options )
-
run( cmd, args )
-
end
-
end
-
end
-
1
require 'optparse'
-
-
1
module Launchy
-
1
class Cli
-
-
1
attr_reader :options
-
1
def initialize
-
@options = {}
-
end
-
-
1
def parser
-
@parser ||= OptionParser.new do |op|
-
op.banner = "Usage: launchy [options] thing-to-launch"
-
-
op.separator ""
-
op.separator "Launch Options:"
-
-
op.on( "-a", "--application APPLICATION",
-
"Explicitly specify the application class to use in the launch") do |app|
-
@options[:application] = app
-
end
-
-
op.on( "-d", "--debug",
-
"Force debug. Output lots of information.") do |d|
-
@options[:debug] = 'true'
-
end
-
-
op.on( "-e", "--engine RUBY_ENGINE",
-
"Force launchy to behave as if it was on a particular ruby engine.") do |e|
-
@options[:ruby_engine] = e
-
end
-
-
op.on( "-n", "--dry-run", "Don't launchy, print the command to be executed on stdout" ) do |x|
-
@options[:dry_run] = true
-
end
-
-
op.on( "-o", "--host-os HOST_OS",
-
"Force launchy to behave as if it was on a particular host os.") do |os|
-
@options[:host_os] = os
-
end
-
-
-
op.separator ""
-
op.separator "Standard Options:"
-
-
op.on( "-h", "--help", "Print this message.") do |h|
-
$stdout.puts op.to_s
-
exit 0
-
end
-
-
op.on( "-v", "--version", "Output the version of Launchy") do |v|
-
$stdout.puts "Launchy version #{Launchy::VERSION}"
-
exit 0
-
end
-
-
end
-
end
-
-
1
def parse( argv, env )
-
begin
-
parser.parse!( argv )
-
return true
-
rescue ::OptionParser::ParseError => pe
-
$stderr.puts "#{parser.program_name}: #{pe}"
-
$stderr.puts "Try `#{parser.program_name} --help for more information."
-
return false
-
end
-
end
-
-
1
def good_run( argv, env )
-
if parse( argv, env ) then
-
Launchy.open( argv.shift, options )
-
return true
-
else
-
return false
-
end
-
end
-
-
1
def run( argv = ARGV, env = ENV )
-
exit 1 unless good_run( argv, env )
-
end
-
end
-
end
-
1
module Launchy
-
#
-
# This class is deprecated and will be removed
-
#
-
1
class Browser
-
1
def self.run( *args )
-
Browser.new.visit( args[0] )
-
end
-
-
1
def visit( url )
-
_warn "You made a call to a deprecated Launchy API. This call should be changed to 'Launchy.open( uri )'"
-
report_caller_context( caller )
-
-
::Launchy.open( url )
-
end
-
-
1
private
-
-
1
def find_caller_context( stack )
-
caller_file = stack.find do |line|
-
not line.index( __FILE__ )
-
end
-
if caller_file then
-
caller_fname, caller_line, _ = caller_file.split(":")
-
if File.readable?( caller_fname ) then
-
caller_lines = IO.readlines( caller_fname )
-
context = [ caller_file ]
-
context << caller_lines[(caller_line.to_i)-3, 5]
-
return context.flatten
-
end
-
end
-
return []
-
end
-
-
1
def report_caller_context( stack )
-
context = find_caller_context( stack )
-
if context.size > 0 then
-
_warn "I think I was able to find the location that needs to be fixed. Please go look at:"
-
_warn
-
context.each do |line|
-
_warn line.rstrip
-
end
-
_warn
-
_warn "If this is not the case, please file a bug. #{Launchy.bug_report_message}"
-
end
-
end
-
-
1
def _warn( msg = "" )
-
warn "WARNING: #{msg}"
-
end
-
end
-
end
-
1
require 'set'
-
-
1
module Launchy
-
#
-
# Use by either
-
#
-
# class Foo
-
# extend DescendantTracker
-
# end
-
#
-
# or
-
#
-
# class Foo
-
# class << self
-
# include DescendantTracker
-
# end
-
# end
-
#
-
# It will track all the classes that inherit from the extended class and keep
-
# them in a Set that is available via the 'children' method.
-
#
-
1
module DescendantTracker
-
1
def inherited( klass )
-
15
return unless klass.instance_of?( Class )
-
15
self.children << klass
-
end
-
-
#
-
# The list of children that are registered
-
#
-
1
def children
-
15
unless defined? @children
-
5
@children = Set.new
-
end
-
15
return @children
-
end
-
-
#
-
# Find one of the child classes by calling the given method
-
# and passing all the rest of the parameters to that method in
-
# each child
-
1
def find_child( method, *args )
-
klass = children.find do |child|
-
Launchy.log "Checking if class #{child} is the one for #{method}(#{args.join(', ')})}"
-
child.send( method, *args )
-
end
-
end
-
end
-
end
-
1
module Launchy
-
1
module Detect
-
end
-
end
-
-
1
require 'launchy/detect/host_os'
-
1
require 'launchy/detect/host_os_family'
-
1
require 'launchy/detect/ruby_engine'
-
1
require 'launchy/detect/nix_desktop_environment'
-
1
require 'launchy/detect/runner'
-
1
require 'rbconfig'
-
-
1
module Launchy::Detect
-
1
class HostOs
-
-
1
attr_reader :host_os
-
1
alias to_s host_os
-
-
1
def initialize( host_os = nil )
-
@host_os = host_os
-
-
if not @host_os then
-
if @host_os = override_host_os then
-
Launchy.log "Using LAUNCHY_HOST_OS override value of '#{Launchy.host_os}'"
-
else
-
@host_os = default_host_os
-
end
-
end
-
end
-
-
1
def default_host_os
-
::RbConfig::CONFIG['host_os']
-
end
-
-
1
def override_host_os
-
Launchy.host_os
-
end
-
-
end
-
-
end
-
1
module Launchy::Detect
-
# Detect the current host os family
-
#
-
# If the current host familiy cannot be detected then return
-
# HostOsFamily::Unknown
-
1
class HostOsFamily
-
1
class NotFoundError < Launchy::Error; end
-
1
extend ::Launchy::DescendantTracker
-
-
1
class << self
-
-
1
def detect( host_os = HostOs.new )
-
found = find_child( :matches?, host_os )
-
return found.new( host_os ) if found
-
raise NotFoundError, "Unknown OS family for host os '#{host_os}'. #{Launchy.bug_report_message}"
-
end
-
-
1
def matches?( host_os )
-
matching_regex.match( host_os.to_s )
-
end
-
-
1
def windows?() self == Windows; end
-
1
def darwin?() self == Darwin; end
-
1
def nix?() self == Nix; end
-
1
def cygwin?() self == Cygwin; end
-
end
-
-
-
1
attr_reader :host_os
-
1
def initialize( host_os = HostOs.new )
-
@host_os = host_os
-
end
-
-
1
def windows?() self.class.windows?; end
-
1
def darwin?() self.class.darwin?; end
-
1
def nix?() self.class.nix?; end
-
1
def cygwin?() self.class.cygwin?; end
-
-
#---------------------------
-
# All known host os families
-
#---------------------------
-
#
-
1
class Windows < HostOsFamily
-
1
def self.matching_regex
-
/(mingw|mswin|windows)/i
-
end
-
1
def app_list( app ) app.windows_app_list; end
-
end
-
-
1
class Darwin < HostOsFamily
-
1
def self.matching_regex
-
/(darwin|mac os)/i
-
end
-
1
def app_list( app ) app.darwin_app_list; end
-
end
-
-
1
class Nix < HostOsFamily
-
1
def self.matching_regex
-
/(linux|bsd|aix|solaris)/i
-
end
-
1
def app_list( app ) app.nix_app_list; end
-
end
-
-
1
class Cygwin < HostOsFamily
-
1
def self.matching_regex
-
/cygwin/i
-
end
-
1
def app_list( app ) app.cygwin_app_list; end
-
end
-
end
-
end
-
1
module Launchy::Detect
-
#
-
# Detect the current desktop environment for *nix machines
-
# Currently this is Linux centric. The detection is based upon the detection
-
# used by xdg-open from http://portland.freedesktop.org/wiki/XdgUtils
-
1
class NixDesktopEnvironment
-
1
class NotFoundError < Launchy::Error; end
-
-
1
extend ::Launchy::DescendantTracker
-
-
# Detect the current *nix desktop environment
-
#
-
# If the current dekstop environment be detected, the return
-
# NixDekstopEnvironment::Unknown
-
1
def self.detect
-
found = find_child( :is_current_desktop_environment? )
-
Launchy.log("Current Desktop environment not found. #{Launchy.bug_report_message}") unless found
-
return found
-
end
-
-
1
def self.fallback_browsers
-
%w[ firefox seamonkey opera mozilla netscape galeon ]
-
end
-
-
#---------------------------------------
-
# The list of known desktop environments
-
#---------------------------------------
-
-
1
class Kde < NixDesktopEnvironment
-
1
def self.is_current_desktop_environment?
-
ENV['KDE_FULL_SESSION']
-
end
-
-
1
def self.browser
-
'kfmclient'
-
end
-
end
-
-
1
class Gnome < NixDesktopEnvironment
-
1
def self.is_current_desktop_environment?
-
ENV['GNOME_DESKTOP_SESSION_ID']
-
end
-
-
1
def self.browser
-
'gnome-open'
-
end
-
end
-
-
1
class Xfce < NixDesktopEnvironment
-
1
def self.is_current_desktop_environment?
-
if Launchy::Application.find_executable( 'xprop' ) then
-
%x[ xprop -root _DT_SAVE_MODE | grep ' = \"xfce\"$' ].strip.size > 0
-
else
-
false
-
end
-
end
-
-
1
def self.browser
-
'exo-open'
-
end
-
end
-
end
-
end
-
-
1
module Launchy::Detect
-
1
class RubyEngine
-
1
class NotFoundError < Launchy::Error; end
-
-
1
extend ::Launchy::DescendantTracker
-
-
# Detect the current ruby engine.
-
#
-
# If the current ruby engine cannot be detected, the return
-
# RubyEngine::Unknown
-
1
def self.detect( ruby_engine = RubyEngine.new )
-
found = find_child( :is_current_engine?, ruby_engine.to_s )
-
return found if found
-
raise NotFoundError, "#{ruby_engine_error_message( ruby_engine )} #{Launchy.bug_report_message}"
-
end
-
-
1
def self.ruby_engine_error_message( ruby_engine )
-
msg = "Unkonwn RUBY_ENGINE "
-
if ruby_engine then
-
msg += " '#{ruby_engine}'."
-
elsif defined?( RUBY_ENGINE ) then
-
msg += " '#{RUBY_ENGINE}'."
-
else
-
msg = "RUBY_ENGINE not defined for #{RUBY_DESCRIPTION}."
-
end
-
return msg
-
end
-
-
1
def self.is_current_engine?( ruby_engine )
-
return ruby_engine == self.engine_name
-
end
-
-
1
def self.mri?() self == Mri; end
-
1
def self.jruby?() self == Jruby; end
-
1
def self.rbx?() self == Rbx; end
-
1
def self.macruby?() self == MacRuby; end
-
-
1
attr_reader :ruby_engine
-
1
alias to_s ruby_engine
-
1
def initialize( ruby_engine = Launchy.ruby_engine )
-
if ruby_engine then
-
@ruby_engine = ruby_engine
-
else
-
@ruby_engine = defined?( RUBY_ENGINE ) ? RUBY_ENGINE : "ruby"
-
end
-
end
-
-
-
#-------------------------------
-
# The list of known ruby engines
-
#-------------------------------
-
-
#
-
# This is the ruby engine if the RUBY_ENGINE constant is not defined
-
1
class Mri < RubyEngine
-
1
def self.engine_name() "ruby"; end
-
1
def self.is_current_engine?( ruby_engine )
-
if ruby_engine then
-
super( ruby_engine )
-
else
-
return true if not Launchy.ruby_engine and not defined?( RUBY_ENGINE )
-
end
-
end
-
end
-
-
1
class Jruby < RubyEngine
-
1
def self.engine_name() "jruby"; end
-
end
-
-
1
class Rbx < RubyEngine
-
1
def self.engine_name() "rbx"; end
-
end
-
-
1
class MacRuby < RubyEngine
-
1
def self.engine_name() "macruby"; end
-
end
-
end
-
end
-
1
require 'shellwords'
-
-
1
module Launchy::Detect
-
1
class Runner
-
1
class NotFoundError < Launchy::Error; end
-
-
1
extend ::Launchy::DescendantTracker
-
-
# Detect the current command runner
-
#
-
# This will return an instance of the Runner to be used to do the
-
# application launching.
-
#
-
# If a runner cannot be detected then raise Runner::NotFoundError
-
#
-
# The runner rules are, in order:
-
#
-
# 1) If you are on windows, you use the Windows Runner no matter what
-
# 2) If you are using the jruby engine, use the Jruby Runner. Unless rule
-
# (1) took effect
-
# 3) Use Forkable (barring rules (1) and (2))
-
1
def self.detect
-
host_os_family = Launchy::Detect::HostOsFamily.detect
-
ruby_engine = Launchy::Detect::RubyEngine.detect
-
-
return Windows.new if host_os_family.windows?
-
if ruby_engine.jruby? then
-
require 'spoon'
-
return Jruby.new
-
end
-
return Forkable.new
-
end
-
-
#
-
# cut it down to just the shell commands that will be passed to exec or
-
# posix_spawn. The cmd argument is split according to shell rules and the
-
# args are not escaped because they whole set is passed to system as *args
-
# and in that case system shell escaping rules are not done.
-
#
-
1
def shell_commands( cmd, args )
-
cmdline = [ cmd.shellsplit ]
-
cmdline << args.flatten.collect{ |a| a.to_s }
-
return commandline_normalize( cmdline )
-
end
-
-
1
def commandline_normalize( cmdline )
-
c = cmdline.flatten!
-
c = c.find_all { |a| (not a.nil?) and ( a.size > 0 ) }
-
Launchy.log "commandline_normalized => #{c.join(' ')}"
-
return c
-
end
-
-
1
def dry_run( cmd, *args )
-
shell_commands(cmd, args).join(" ")
-
end
-
-
1
def run( cmd, *args )
-
raise Launchy::CommandNotFoundError, "No command found to run with args '#{args.join(' ')}'. If this is unexpected, #{Launchy.bug_report_message}" unless cmd
-
if Launchy.dry_run? then
-
$stdout.puts dry_run( cmd, *args )
-
else
-
wet_run( cmd, *args )
-
end
-
end
-
-
-
#---------------------------------------
-
# The list of known runners
-
#---------------------------------------
-
-
1
class Windows < Runner
-
-
1
def all_args( cmd, *args )
-
args = [ 'cmd', '/c', *shell_commands( cmd, *args ) ]
-
Launchy.log "Windows: all_args => #{args.inspect}"
-
return args
-
end
-
-
1
def dry_run( cmd, *args )
-
all_args( cmd, *args ).join(" ")
-
end
-
-
# escape the reserved shell characters in windows command shell
-
# http://technet.microsoft.com/en-us/library/cc723564.aspx
-
1
def shell_commands( cmd, *args )
-
cmdline = [ cmd.shellsplit ]
-
cmdline << args.flatten.collect { |a| a.to_s.gsub(/([&|()<>^])/, "^\\1") }
-
return commandline_normalize( cmdline )
-
end
-
-
1
def wet_run( cmd, *args )
-
system( *all_args( cmd, *args ) )
-
end
-
end
-
-
1
class Jruby < Runner
-
1
def wet_run( cmd, *args )
-
Spoon.spawnp( *shell_commands( cmd, *args ) )
-
end
-
end
-
-
1
class Forkable < Runner
-
1
def wet_run( cmd, *args )
-
child_pid = fork do
-
exec( *shell_commands( cmd, *args ))
-
exit!
-
end
-
Process.detach( child_pid )
-
end
-
end
-
end
-
end
-
1
module Launchy
-
1
class Error < ::StandardError; end
-
1
class ApplicationNotFoundError < Error; end
-
1
class CommandNotFoundError < Error; end
-
end
-
1
module Launchy
-
1
VERSION = "2.1.0"
-
-
1
module Version
-
-
1
MAJOR = Integer(VERSION.split('.')[0])
-
1
MINOR = Integer(VERSION.split('.')[1])
-
1
PATCH = Integer(VERSION.split('.')[2])
-
-
1
def self.to_a
-
[MAJOR, MINOR, PATCH]
-
end
-
-
1
def self.to_s
-
VERSION
-
end
-
end
-
end
-
# -*- coding: utf-8 -*-
-
# Modify the PATH on windows so that the external DLLs will get loaded.
-
-
1
require 'rbconfig'
-
ENV['PATH'] = [File.expand_path(
-
File.join(File.dirname(__FILE__), "..", "ext", "nokogiri")
-
1
), ENV['PATH']].compact.join(';') if RbConfig::CONFIG['host_os'] =~ /(mswin|mingw)/i
-
-
1
if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
-
# The line below caused a problem on non-GAE rack environment.
-
# unless defined?(JRuby::Rack::VERSION) || defined?(AppEngine::ApiProxy)
-
#
-
# However, simply cutting defined?(JRuby::Rack::VERSION) off resulted in
-
# an unable-to-load-nokogiri problem. Thus, now, Nokogiri checks the presense
-
# of appengine-rack.jar in $LOAD_PATH. If Nokogiri is on GAE, Nokogiri
-
# should skip loading xml jars. This is because those are in WEB-INF/lib and
-
# already set in the classpath.
-
unless $LOAD_PATH.to_s.include?("appengine-rack")
-
require 'isorelax.jar'
-
require 'jing.jar'
-
require 'nekohtml.jar'
-
require 'nekodtd.jar'
-
require 'xercesImpl.jar'
-
end
-
end
-
-
1
require 'nokogiri/nokogiri'
-
1
require 'nokogiri/version'
-
1
require 'nokogiri/syntax_error'
-
1
require 'nokogiri/xml'
-
1
require 'nokogiri/xslt'
-
1
require 'nokogiri/html'
-
1
require 'nokogiri/decorators/slop'
-
1
require 'nokogiri/css'
-
1
require 'nokogiri/html/builder'
-
-
# Nokogiri parses and searches XML/HTML very quickly, and also has
-
# correctly implemented CSS3 selector support as well as XPath support.
-
#
-
# Parsing a document returns either a Nokogiri::XML::Document, or a
-
# Nokogiri::HTML::Document depending on the kind of document you parse.
-
#
-
# Here is an example:
-
#
-
# require 'nokogiri'
-
# require 'open-uri'
-
#
-
# # Get a Nokogiri::HTML:Document for the page we’re interested in...
-
#
-
# doc = Nokogiri::HTML(open('http://www.google.com/search?q=tenderlove'))
-
#
-
# # Do funky things with it using Nokogiri::XML::Node methods...
-
#
-
# ####
-
# # Search for nodes by css
-
# doc.css('h3.r a.l').each do |link|
-
# puts link.content
-
# end
-
#
-
# See Nokogiri::XML::Node#css for more information about CSS searching.
-
# See Nokogiri::XML::Node#xpath for more information about XPath searching.
-
1
module Nokogiri
-
1
class << self
-
###
-
# Parse an HTML or XML document. +string+ contains the document.
-
1
def parse string, url = nil, encoding = nil, options = nil
-
doc =
-
if string.respond_to?(:read) ||
-
string =~ /^\s*<[^Hh>]*html/i # Probably html
-
Nokogiri.HTML(
-
string,
-
url,
-
encoding, options || XML::ParseOptions::DEFAULT_HTML
-
)
-
else
-
Nokogiri.XML(string, url, encoding,
-
options || XML::ParseOptions::DEFAULT_XML)
-
end
-
yield doc if block_given?
-
doc
-
end
-
-
###
-
# Create a new Nokogiri::XML::DocumentFragment
-
1
def make input = nil, opts = {}, &blk
-
if input
-
Nokogiri::HTML.fragment(input).children.first
-
else
-
Nokogiri(&blk)
-
end
-
end
-
-
###
-
# Parse a document and add the Slop decorator. The Slop decorator
-
# implements method_missing such that methods may be used instead of CSS
-
# or XPath. For example:
-
#
-
# doc = Nokogiri::Slop(<<-eohtml)
-
# <html>
-
# <body>
-
# <p>first</p>
-
# <p>second</p>
-
# </body>
-
# </html>
-
# eohtml
-
# assert_equal('second', doc.html.body.p[1].text)
-
#
-
1
def Slop(*args, &block)
-
Nokogiri(*args, &block).slop!
-
end
-
end
-
end
-
-
###
-
# Parser a document contained in +args+. Nokogiri will try to guess what
-
# type of document you are attempting to parse. For more information, see
-
# Nokogiri.parse
-
#
-
# To specify the type of document, use Nokogiri.XML or Nokogiri.HTML.
-
1
def Nokogiri(*args, &block)
-
if block_given?
-
builder = Nokogiri::HTML::Builder.new(&block)
-
return builder.doc.root
-
else
-
Nokogiri.parse(*args)
-
end
-
end
-
1
require 'nokogiri/css/node'
-
1
require 'nokogiri/css/xpath_visitor'
-
1
x = $-w
-
1
$-w = false
-
1
require 'nokogiri/css/parser'
-
1
$-w = x
-
-
1
require 'nokogiri/css/tokenizer'
-
1
require 'nokogiri/css/syntax_error'
-
-
1
module Nokogiri
-
1
module CSS
-
1
class << self
-
###
-
# Parse this CSS selector in +selector+. Returns an AST.
-
1
def parse selector
-
Parser.new.parse selector
-
end
-
-
###
-
# Get the XPath for +selector+.
-
1
def xpath_for selector, options={}
-
Parser.new(options[:ns] || {}).xpath_for selector, options
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module CSS
-
1
class Node
-
# Get the type of this node
-
1
attr_accessor :type
-
# Get the value of this node
-
1
attr_accessor :value
-
-
# Create a new Node with +type+ and +value+
-
1
def initialize type, value
-
@type = type
-
@value = value
-
end
-
-
# Accept +visitor+
-
1
def accept visitor
-
visitor.send(:"visit_#{type.to_s.downcase}", self)
-
end
-
-
###
-
# Convert this CSS node to xpath with +prefix+ using +visitor+
-
1
def to_xpath prefix = '//', visitor = XPathVisitor.new
-
self.preprocess!
-
prefix + visitor.accept(self)
-
end
-
-
# Preprocess this node tree
-
1
def preprocess!
-
### Deal with nth-child
-
matches = find_by_type(
-
[:CONDITIONAL_SELECTOR,
-
[:ELEMENT_NAME],
-
[:PSEUDO_CLASS,
-
[:FUNCTION]
-
]
-
]
-
)
-
matches.each do |match|
-
if match.value[1].value[0].value[0] =~ /^nth-(last-)?child/
-
tag_name = match.value[0].value.first
-
match.value[0].value = ['*']
-
match.value[1] = Node.new(:COMBINATOR, [
-
match.value[1].value[0],
-
Node.new(:FUNCTION, ['self(', tag_name])
-
])
-
end
-
end
-
-
### Deal with first-child, last-child
-
matches = find_by_type(
-
[:CONDITIONAL_SELECTOR,
-
[:ELEMENT_NAME], [:PSEUDO_CLASS]
-
])
-
matches.each do |match|
-
if ['first-child', 'last-child'].include?(match.value[1].value.first)
-
which = match.value[1].value.first.gsub(/-\w*$/, '')
-
tag_name = match.value[0].value.first
-
match.value[0].value = ['*']
-
match.value[1] = Node.new(:COMBINATOR, [
-
Node.new(:FUNCTION, ["#{which}("]),
-
Node.new(:FUNCTION, ['self(', tag_name])
-
])
-
elsif 'only-child' == match.value[1].value.first
-
tag_name = match.value[0].value.first
-
match.value[0].value = ['*']
-
match.value[1] = Node.new(:COMBINATOR, [
-
Node.new(:FUNCTION, ["#{match.value[1].value.first}("]),
-
Node.new(:FUNCTION, ['self(', tag_name])
-
])
-
end
-
end
-
-
self
-
end
-
-
# Find a node by type using +types+
-
1
def find_by_type types
-
matches = []
-
matches << self if to_type == types
-
@value.each do |v|
-
matches += v.find_by_type(types) if v.respond_to?(:find_by_type)
-
end
-
matches
-
end
-
-
# Convert to_type
-
1
def to_type
-
[@type] + @value.map { |n|
-
n.to_type if n.respond_to?(:to_type)
-
}.compact
-
end
-
-
# Convert to array
-
1
def to_a
-
[@type] + @value.map { |n| n.respond_to?(:to_a) ? n.to_a : [n] }
-
end
-
end
-
end
-
end
-
#
-
# DO NOT MODIFY!!!!
-
# This file is automatically generated by Racc 1.4.7
-
# from Racc grammer file "".
-
#
-
-
1
require 'racc/parser.rb'
-
-
-
1
require 'nokogiri/css/parser_extras'
-
1
module Nokogiri
-
1
module CSS
-
1
class Parser < Racc::Parser
-
##### State transition tables begin ###
-
-
1
racc_action_table = [
-
5, 57, 28, 23, 13, 25, 61, 58, 5, 1,
-
6, 60, 13, 58, 66, 24, 17, 1, 83, 20,
-
94, 93, 6, 13, 9, 11, 14, 20, 17, 42,
-
6, 5, 9, 11, 14, 13, 17, 76, 42, 5,
-
1, 6, 65, 13, 11, 62, 63, 17, 1, 60,
-
20, 5, 63, 6, 84, 9, 11, 14, 20, 17,
-
1, 6, 30, 9, 11, 14, 13, 17, 13, 85,
-
20, 1, 64, 13, 59, 9, 88, 13, 13, 89,
-
28, 54, 13, 55, 6, 42, 6, 11, 14, 11,
-
17, 6, 17, 45, 11, 6, 6, 17, 11, 11,
-
6, 17, 17, 11, 5, 92, 17, 69, 71, 28,
-
54, 22, 55, 50, 28, 54, 95, 55, 70, 72,
-
73, -24, 75, 20, 69, 71, 67, 97, 9, 98,
-
nil, nil, 34, 36, 38, 70, 72, 73, nil, 75,
-
nil, nil, 33, 67, 35, 37, 28, 54, nil, 55 ]
-
-
1
racc_action_check = [
-
0, 21, 5, 5, 0, 5, 23, 43, 9, 0,
-
15, 23, 9, 21, 28, 5, 15, 9, 43, 0,
-
74, 74, 0, 8, 0, 0, 0, 9, 0, 40,
-
9, 32, 9, 9, 9, 32, 9, 30, 8, 58,
-
32, 8, 27, 58, 8, 25, 25, 8, 58, 54,
-
32, 6, 55, 32, 56, 32, 32, 32, 58, 32,
-
6, 58, 6, 58, 58, 58, 42, 58, 80, 57,
-
6, 42, 26, 10, 22, 6, 61, 19, 18, 62,
-
60, 60, 16, 60, 42, 12, 80, 42, 42, 80,
-
42, 10, 80, 11, 10, 19, 18, 10, 19, 18,
-
16, 19, 18, 16, 17, 68, 16, 31, 31, 20,
-
20, 4, 20, 17, 63, 63, 77, 63, 31, 31,
-
31, 1, 31, 17, 29, 29, 31, 82, 17, 89,
-
nil, nil, 7, 7, 7, 29, 29, 29, nil, 29,
-
nil, nil, 7, 29, 7, 7, 66, 66, nil, 66 ]
-
-
1
racc_action_pointer = [
-
-2, 92, nil, nil, 82, -8, 49, 125, 17, 6,
-
67, 82, 64, nil, nil, -14, 76, 102, 72, 71,
-
99, 1, 63, -1, nil, 34, 49, 19, 2, 121,
-
12, 104, 29, nil, nil, nil, nil, nil, nil, nil,
-
8, nil, 60, -5, nil, nil, nil, nil, nil, nil,
-
nil, nil, nil, nil, 37, 40, 31, 69, 37, nil,
-
70, 63, 72, 104, nil, nil, 136, nil, 80, nil,
-
nil, nil, nil, nil, 10, nil, nil, 91, nil, nil,
-
62, nil, 104, nil, nil, nil, nil, nil, nil, 116,
-
nil, nil, nil, nil, nil, nil, nil, nil, nil ]
-
-
1
racc_action_default = [
-
-25, -23, -20, -2, -70, -70, -25, -18, -46, -25,
-
-51, -70, -16, -55, -21, -12, -54, -70, -53, -52,
-
-70, -70, -70, -39, -29, -37, -70, -70, -38, -58,
-
-70, -58, -25, -5, -3, -8, -4, -7, -6, -9,
-
-45, -11, -25, -70, -47, -19, -15, -13, -14, -50,
-
-44, -43, -49, -48, -39, -37, -70, -70, -25, -22,
-
-70, -70, -42, -70, -30, -31, -70, -59, -70, -64,
-
-60, -65, -61, -62, -70, -63, -28, -70, -17, -10,
-
-67, -69, -70, -33, -32, 99, -1, -36, -41, -70,
-
-34, -35, -26, -56, -57, -27, -68, -66, -40 ]
-
-
1
racc_goto_table = [
-
40, 26, 44, 41, 78, 21, 31, 46, 49, 29,
-
52, 53, 47, 68, 43, 77, 56, 51, 48, 39,
-
80, 32, 27, 82, nil, nil, nil, nil, nil, nil,
-
86, nil, nil, nil, 81, 79, nil, nil, nil, nil,
-
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
-
nil, nil, nil, nil, nil, nil, 87, nil, nil, 90,
-
nil, nil, 91, nil, nil, nil, nil, nil, nil, nil,
-
nil, nil, 96 ]
-
-
1
racc_goto_check = [
-
7, 16, 7, 8, 2, 1, 9, 8, 7, 13,
-
7, 7, 10, 15, 1, 15, 16, 9, 11, 6,
-
5, 3, 17, 20, nil, nil, nil, nil, nil, nil,
-
2, nil, nil, nil, 7, 8, nil, nil, nil, nil,
-
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
-
nil, nil, nil, nil, nil, nil, 16, nil, nil, 16,
-
nil, nil, 16, nil, nil, nil, nil, nil, nil, nil,
-
nil, nil, 7 ]
-
-
1
racc_goto_pointer = [
-
nil, 5, -28, 14, nil, -22, 11, -8, -5, 0,
-
-3, 3, nil, 3, nil, -16, -4, 17, nil, nil,
-
-19 ]
-
-
1
racc_goto_default = [
-
nil, nil, 3, nil, 7, 8, nil, 12, nil, 15,
-
16, 18, 19, 2, 4, nil, nil, nil, 10, 74,
-
nil ]
-
-
1
racc_reduce_table = [
-
0, 0, :racc_error,
-
3, 32, :_reduce_1,
-
1, 32, :_reduce_2,
-
1, 34, :_reduce_3,
-
1, 34, :_reduce_4,
-
1, 34, :_reduce_5,
-
1, 34, :_reduce_6,
-
1, 34, :_reduce_7,
-
1, 34, :_reduce_8,
-
2, 35, :_reduce_9,
-
3, 35, :_reduce_10,
-
2, 35, :_reduce_11,
-
1, 35, :_reduce_none,
-
2, 35, :_reduce_13,
-
2, 35, :_reduce_14,
-
2, 35, :_reduce_15,
-
1, 35, :_reduce_16,
-
3, 33, :_reduce_17,
-
1, 33, :_reduce_none,
-
2, 43, :_reduce_19,
-
1, 36, :_reduce_none,
-
1, 36, :_reduce_21,
-
3, 44, :_reduce_22,
-
1, 44, :_reduce_23,
-
1, 45, :_reduce_24,
-
0, 45, :_reduce_none,
-
4, 42, :_reduce_26,
-
4, 42, :_reduce_27,
-
3, 42, :_reduce_28,
-
2, 40, :_reduce_29,
-
3, 40, :_reduce_30,
-
3, 40, :_reduce_31,
-
3, 40, :_reduce_32,
-
3, 40, :_reduce_33,
-
3, 47, :_reduce_34,
-
3, 47, :_reduce_35,
-
3, 47, :_reduce_36,
-
1, 47, :_reduce_none,
-
1, 47, :_reduce_none,
-
1, 47, :_reduce_39,
-
4, 48, :_reduce_40,
-
3, 48, :_reduce_41,
-
2, 48, :_reduce_42,
-
2, 41, :_reduce_43,
-
2, 41, :_reduce_44,
-
1, 37, :_reduce_none,
-
0, 37, :_reduce_none,
-
2, 38, :_reduce_47,
-
2, 38, :_reduce_48,
-
2, 38, :_reduce_49,
-
2, 38, :_reduce_50,
-
1, 38, :_reduce_none,
-
1, 38, :_reduce_none,
-
1, 38, :_reduce_none,
-
1, 38, :_reduce_none,
-
1, 49, :_reduce_55,
-
2, 46, :_reduce_56,
-
2, 46, :_reduce_57,
-
0, 46, :_reduce_none,
-
1, 50, :_reduce_59,
-
1, 50, :_reduce_60,
-
1, 50, :_reduce_61,
-
1, 50, :_reduce_62,
-
1, 50, :_reduce_63,
-
1, 50, :_reduce_64,
-
1, 50, :_reduce_65,
-
3, 39, :_reduce_66,
-
1, 51, :_reduce_none,
-
2, 51, :_reduce_none,
-
1, 51, :_reduce_none ]
-
-
1
racc_reduce_n = 70
-
-
1
racc_shift_n = 99
-
-
1
racc_token_table = {
-
false => 0,
-
:error => 1,
-
:FUNCTION => 2,
-
:INCLUDES => 3,
-
:DASHMATCH => 4,
-
:LBRACE => 5,
-
:HASH => 6,
-
:PLUS => 7,
-
:GREATER => 8,
-
:S => 9,
-
:STRING => 10,
-
:IDENT => 11,
-
:COMMA => 12,
-
:NUMBER => 13,
-
:PREFIXMATCH => 14,
-
:SUFFIXMATCH => 15,
-
:SUBSTRINGMATCH => 16,
-
:TILDE => 17,
-
:NOT_EQUAL => 18,
-
:SLASH => 19,
-
:DOUBLESLASH => 20,
-
:NOT => 21,
-
:EQUAL => 22,
-
:RPAREN => 23,
-
:LSQUARE => 24,
-
:RSQUARE => 25,
-
:HAS => 26,
-
"." => 27,
-
"*" => 28,
-
"|" => 29,
-
":" => 30 }
-
-
1
racc_nt_base = 31
-
-
1
racc_use_result_var = true
-
-
1
Racc_arg = [
-
racc_action_table,
-
racc_action_check,
-
racc_action_default,
-
racc_action_pointer,
-
racc_goto_table,
-
racc_goto_check,
-
racc_goto_default,
-
racc_goto_pointer,
-
racc_nt_base,
-
racc_reduce_table,
-
racc_token_table,
-
racc_shift_n,
-
racc_reduce_n,
-
racc_use_result_var ]
-
-
1
Racc_token_to_s_table = [
-
"$end",
-
"error",
-
"FUNCTION",
-
"INCLUDES",
-
"DASHMATCH",
-
"LBRACE",
-
"HASH",
-
"PLUS",
-
"GREATER",
-
"S",
-
"STRING",
-
"IDENT",
-
"COMMA",
-
"NUMBER",
-
"PREFIXMATCH",
-
"SUFFIXMATCH",
-
"SUBSTRINGMATCH",
-
"TILDE",
-
"NOT_EQUAL",
-
"SLASH",
-
"DOUBLESLASH",
-
"NOT",
-
"EQUAL",
-
"RPAREN",
-
"LSQUARE",
-
"RSQUARE",
-
"HAS",
-
"\".\"",
-
"\"*\"",
-
"\"|\"",
-
"\":\"",
-
"$start",
-
"selector",
-
"simple_selector_1toN",
-
"combinator",
-
"simple_selector",
-
"element_name",
-
"hcap_0toN",
-
"hcap_1toN",
-
"negation",
-
"function",
-
"pseudo",
-
"attrib",
-
"class",
-
"namespaced_ident",
-
"namespace",
-
"attrib_val_0or1",
-
"expr",
-
"an_plus_b",
-
"attribute_id",
-
"eql_incl_dash",
-
"negation_arg" ]
-
-
1
Racc_debug_parser = false
-
-
##### State transition tables end #####
-
-
# reduce 0 omitted
-
-
1
def _reduce_1(val, _values, result)
-
result = [val.first, val.last].flatten
-
-
result
-
end
-
-
1
def _reduce_2(val, _values, result)
-
result = val.flatten
-
result
-
end
-
-
1
def _reduce_3(val, _values, result)
-
result = :DIRECT_ADJACENT_SELECTOR
-
result
-
end
-
-
1
def _reduce_4(val, _values, result)
-
result = :CHILD_SELECTOR
-
result
-
end
-
-
1
def _reduce_5(val, _values, result)
-
result = :PRECEDING_SELECTOR
-
result
-
end
-
-
1
def _reduce_6(val, _values, result)
-
result = :DESCENDANT_SELECTOR
-
result
-
end
-
-
1
def _reduce_7(val, _values, result)
-
result = :DESCENDANT_SELECTOR
-
result
-
end
-
-
1
def _reduce_8(val, _values, result)
-
result = :CHILD_SELECTOR
-
result
-
end
-
-
1
def _reduce_9(val, _values, result)
-
result = if val[1].nil?
-
val.first
-
else
-
Node.new(:CONDITIONAL_SELECTOR, [val.first, val[1]])
-
end
-
-
result
-
end
-
-
1
def _reduce_10(val, _values, result)
-
result = Node.new(:CONDITIONAL_SELECTOR,
-
[
-
val.first,
-
Node.new(:COMBINATOR, [val[1], val.last])
-
]
-
)
-
-
result
-
end
-
-
1
def _reduce_11(val, _values, result)
-
result = Node.new(:CONDITIONAL_SELECTOR, val)
-
-
result
-
end
-
-
# reduce 12 omitted
-
-
1
def _reduce_13(val, _values, result)
-
result = Node.new(:CONDITIONAL_SELECTOR, val)
-
-
result
-
end
-
-
1
def _reduce_14(val, _values, result)
-
result = Node.new(:CONDITIONAL_SELECTOR, val)
-
-
result
-
end
-
-
1
def _reduce_15(val, _values, result)
-
result = Node.new(:CONDITIONAL_SELECTOR,
-
[
-
Node.new(:ELEMENT_NAME, ['*']),
-
Node.new(:COMBINATOR, val)
-
]
-
)
-
-
result
-
end
-
-
1
def _reduce_16(val, _values, result)
-
result = Node.new(:CONDITIONAL_SELECTOR,
-
[Node.new(:ELEMENT_NAME, ['*']), val.first]
-
)
-
-
result
-
end
-
-
1
def _reduce_17(val, _values, result)
-
result = Node.new(val[1], [val.first, val.last])
-
-
result
-
end
-
-
# reduce 18 omitted
-
-
1
def _reduce_19(val, _values, result)
-
result = Node.new(:CLASS_CONDITION, [val[1]])
-
result
-
end
-
-
# reduce 20 omitted
-
-
1
def _reduce_21(val, _values, result)
-
result = Node.new(:ELEMENT_NAME, val)
-
result
-
end
-
-
1
def _reduce_22(val, _values, result)
-
result = Node.new(:ELEMENT_NAME,
-
[[val.first, val.last].compact.join(':')]
-
)
-
-
result
-
end
-
-
1
def _reduce_23(val, _values, result)
-
name = @namespaces.key?('xmlns') ? "xmlns:#{val.first}" : val.first
-
result = Node.new(:ELEMENT_NAME, [name])
-
-
result
-
end
-
-
1
def _reduce_24(val, _values, result)
-
result = val[0]
-
result
-
end
-
-
# reduce 25 omitted
-
-
1
def _reduce_26(val, _values, result)
-
result = Node.new(:ATTRIBUTE_CONDITION,
-
[val[1]] + (val[2] || [])
-
)
-
-
result
-
end
-
-
1
def _reduce_27(val, _values, result)
-
result = Node.new(:ATTRIBUTE_CONDITION,
-
[val[1]] + (val[2] || [])
-
)
-
-
result
-
end
-
-
1
def _reduce_28(val, _values, result)
-
# Non standard, but hpricot supports it.
-
result = Node.new(:PSEUDO_CLASS,
-
[Node.new(:FUNCTION, ['nth-child(', val[1]])]
-
)
-
-
result
-
end
-
-
1
def _reduce_29(val, _values, result)
-
result = Node.new(:FUNCTION, [val.first.strip])
-
-
result
-
end
-
-
1
def _reduce_30(val, _values, result)
-
result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
-
-
result
-
end
-
-
1
def _reduce_31(val, _values, result)
-
result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
-
-
result
-
end
-
-
1
def _reduce_32(val, _values, result)
-
result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
-
-
result
-
end
-
-
1
def _reduce_33(val, _values, result)
-
result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
-
-
result
-
end
-
-
1
def _reduce_34(val, _values, result)
-
result = [val.first, val.last]
-
result
-
end
-
-
1
def _reduce_35(val, _values, result)
-
result = [val.first, val.last]
-
result
-
end
-
-
1
def _reduce_36(val, _values, result)
-
result = [val.first, val.last]
-
result
-
end
-
-
# reduce 37 omitted
-
-
# reduce 38 omitted
-
-
1
def _reduce_39(val, _values, result)
-
if val[0] == 'even'
-
val = ["2","n","+","0"]
-
result = Node.new(:AN_PLUS_B, val)
-
elsif val[0] == 'odd'
-
val = ["2","n","+","1"]
-
result = Node.new(:AN_PLUS_B, val)
-
else
-
# This is not CSS standard. It allows us to support this:
-
# assert_xpath("//a[foo(., @href)]", @parser.parse('a:foo(@href)'))
-
# assert_xpath("//a[foo(., @a, b)]", @parser.parse('a:foo(@a, b)'))
-
# assert_xpath("//a[foo(., a, 10)]", @parser.parse('a:foo(a, 10)'))
-
result = val
-
end
-
-
result
-
end
-
-
1
def _reduce_40(val, _values, result)
-
if val[1] == 'n'
-
result = Node.new(:AN_PLUS_B, val)
-
else
-
raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
-
end
-
-
result
-
end
-
-
1
def _reduce_41(val, _values, result)
-
# n+3, -n+3
-
if val[0] == 'n'
-
val.unshift("1")
-
result = Node.new(:AN_PLUS_B, val)
-
elsif val[0] == '-n'
-
val[0] = 'n'
-
val.unshift("-1")
-
result = Node.new(:AN_PLUS_B, val)
-
else
-
raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
-
end
-
-
result
-
end
-
-
1
def _reduce_42(val, _values, result)
-
if val[1] == 'n'
-
val << "+"
-
val << "0"
-
result = Node.new(:AN_PLUS_B, val)
-
else
-
raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
-
end
-
-
result
-
end
-
-
1
def _reduce_43(val, _values, result)
-
result = Node.new(:PSEUDO_CLASS, [val[1]])
-
-
result
-
end
-
-
1
def _reduce_44(val, _values, result)
-
result = Node.new(:PSEUDO_CLASS, [val[1]])
-
result
-
end
-
-
# reduce 45 omitted
-
-
# reduce 46 omitted
-
-
1
def _reduce_47(val, _values, result)
-
result = Node.new(:COMBINATOR, val)
-
-
result
-
end
-
-
1
def _reduce_48(val, _values, result)
-
result = Node.new(:COMBINATOR, val)
-
-
result
-
end
-
-
1
def _reduce_49(val, _values, result)
-
result = Node.new(:COMBINATOR, val)
-
-
result
-
end
-
-
1
def _reduce_50(val, _values, result)
-
result = Node.new(:COMBINATOR, val)
-
-
result
-
end
-
-
# reduce 51 omitted
-
-
# reduce 52 omitted
-
-
# reduce 53 omitted
-
-
# reduce 54 omitted
-
-
1
def _reduce_55(val, _values, result)
-
result = Node.new(:ID, val)
-
result
-
end
-
-
1
def _reduce_56(val, _values, result)
-
result = [val.first, val[1]]
-
result
-
end
-
-
1
def _reduce_57(val, _values, result)
-
result = [val.first, val[1]]
-
result
-
end
-
-
# reduce 58 omitted
-
-
1
def _reduce_59(val, _values, result)
-
result = :equal
-
result
-
end
-
-
1
def _reduce_60(val, _values, result)
-
result = :prefix_match
-
result
-
end
-
-
1
def _reduce_61(val, _values, result)
-
result = :suffix_match
-
result
-
end
-
-
1
def _reduce_62(val, _values, result)
-
result = :substring_match
-
result
-
end
-
-
1
def _reduce_63(val, _values, result)
-
result = :not_equal
-
result
-
end
-
-
1
def _reduce_64(val, _values, result)
-
result = :includes
-
result
-
end
-
-
1
def _reduce_65(val, _values, result)
-
result = :dash_match
-
result
-
end
-
-
1
def _reduce_66(val, _values, result)
-
result = Node.new(:NOT, [val[1]])
-
-
result
-
end
-
-
# reduce 67 omitted
-
-
# reduce 68 omitted
-
-
# reduce 69 omitted
-
-
1
def _reduce_none(val, _values, result)
-
val[0]
-
end
-
-
end # class Parser
-
end # module CSS
-
end # module Nokogiri
-
1
require 'thread'
-
-
1
module Nokogiri
-
1
module CSS
-
1
class Parser < Racc::Parser
-
1
@cache_on = true
-
1
@cache = {}
-
1
@mutex = Mutex.new
-
-
1
class << self
-
# Turn on CSS parse caching
-
1
attr_accessor :cache_on
-
1
alias :cache_on? :cache_on
-
1
alias :set_cache :cache_on=
-
-
# Get the css selector in +string+ from the cache
-
1
def [] string
-
return unless @cache_on
-
@mutex.synchronize { @cache[string] }
-
end
-
-
# Set the css selector in +string+ in the cache to +value+
-
1
def []= string, value
-
return value unless @cache_on
-
@mutex.synchronize { @cache[string] = value }
-
end
-
-
# Clear the cache
-
1
def clear_cache
-
@mutex.synchronize { @cache = {} }
-
end
-
-
# Execute +block+ without cache
-
1
def without_cache &block
-
tmp = @cache_on
-
@cache_on = false
-
block.call
-
@cache_on = tmp
-
end
-
-
###
-
# Parse this CSS selector in +selector+. Returns an AST.
-
1
def parse selector
-
@warned ||= false
-
unless @warned
-
$stderr.puts('Nokogiri::CSS::Parser.parse is deprecated, call Nokogiri::CSS.parse(), this will be removed August 1st or version 1.4.0 (whichever is first)')
-
@warned = true
-
end
-
new.parse selector
-
end
-
end
-
-
# Create a new CSS parser with respect to +namespaces+
-
1
def initialize namespaces = {}
-
@tokenizer = Tokenizer.new
-
@namespaces = namespaces
-
super()
-
end
-
-
1
def parse string
-
@tokenizer.scan_setup string
-
do_parse
-
end
-
-
1
def next_token
-
@tokenizer.next_token
-
end
-
-
# Get the xpath for +string+ using +options+
-
1
def xpath_for string, options={}
-
key = "#{string}#{options[:ns]}#{options[:prefix]}"
-
v = self.class[key]
-
return v if v
-
-
args = [
-
options[:prefix] || '//',
-
options[:visitor] || XPathVisitor.new
-
]
-
self.class[key] = parse(string).map { |ast|
-
ast.to_xpath(*args)
-
}
-
end
-
-
# On CSS parser error, raise an exception
-
1
def on_error error_token_id, error_value, value_stack
-
after = value_stack.compact.last
-
raise SyntaxError.new("unexpected '#{error_value}' after '#{after}'")
-
end
-
end
-
end
-
end
-
1
require 'nokogiri/syntax_error'
-
1
module Nokogiri
-
1
module CSS
-
1
class SyntaxError < ::Nokogiri::SyntaxError
-
end
-
end
-
end
-
#--
-
# DO NOT MODIFY!!!!
-
# This file is automatically generated by rex 1.0.5
-
# from lexical definition file "lib/nokogiri/css/tokenizer.rex".
-
#++
-
-
1
module Nokogiri
-
1
module CSS
-
1
class Tokenizer # :nodoc:
-
1
require 'strscan'
-
-
1
class ScanError < StandardError ; end
-
-
1
attr_reader :lineno
-
1
attr_reader :filename
-
1
attr_accessor :state
-
-
1
def scan_setup(str)
-
@ss = StringScanner.new(str)
-
@lineno = 1
-
@state = nil
-
end
-
-
1
def action
-
yield
-
end
-
-
1
def scan_str(str)
-
scan_setup(str)
-
do_parse
-
end
-
1
alias :scan :scan_str
-
-
1
def load_file( filename )
-
@filename = filename
-
open(filename, "r") do |f|
-
scan_setup(f.read)
-
end
-
end
-
-
1
def scan_file( filename )
-
load_file(filename)
-
do_parse
-
end
-
-
-
1
def next_token
-
return if @ss.eos?
-
-
# skips empty actions
-
until token = _next_token or @ss.eos?; end
-
token
-
end
-
-
1
def _next_token
-
text = @ss.peek(1)
-
@lineno += 1 if text == "\n"
-
token = case @state
-
when nil
-
case
-
when (text = @ss.scan(/has\([\s]*/))
-
action { [:HAS, text] }
-
-
when (text = @ss.scan(/[-@]?([_A-Za-z]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])([_A-Za-z0-9-]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*\([\s]*/))
-
action { [:FUNCTION, text] }
-
-
when (text = @ss.scan(/[-@]?([_A-Za-z]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])([_A-Za-z0-9-]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*/))
-
action { [:IDENT, text] }
-
-
when (text = @ss.scan(/\#([_A-Za-z0-9-]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])+/))
-
action { [:HASH, text] }
-
-
when (text = @ss.scan(/[\s]*~=[\s]*/))
-
action { [:INCLUDES, text] }
-
-
when (text = @ss.scan(/[\s]*\|=[\s]*/))
-
action { [:DASHMATCH, text] }
-
-
when (text = @ss.scan(/[\s]*\^=[\s]*/))
-
action { [:PREFIXMATCH, text] }
-
-
when (text = @ss.scan(/[\s]*\$=[\s]*/))
-
action { [:SUFFIXMATCH, text] }
-
-
when (text = @ss.scan(/[\s]*\*=[\s]*/))
-
action { [:SUBSTRINGMATCH, text] }
-
-
when (text = @ss.scan(/[\s]*!=[\s]*/))
-
action { [:NOT_EQUAL, text] }
-
-
when (text = @ss.scan(/[\s]*=[\s]*/))
-
action { [:EQUAL, text] }
-
-
when (text = @ss.scan(/[\s]*\)/))
-
action { [:RPAREN, text] }
-
-
when (text = @ss.scan(/[\s]*\[[\s]*/))
-
action { [:LSQUARE, text] }
-
-
when (text = @ss.scan(/[\s]*\]/))
-
action { [:RSQUARE, text] }
-
-
when (text = @ss.scan(/[\s]*\+[\s]*/))
-
action { [:PLUS, text] }
-
-
when (text = @ss.scan(/[\s]*>[\s]*/))
-
action { [:GREATER, text] }
-
-
when (text = @ss.scan(/[\s]*,[\s]*/))
-
action { [:COMMA, text] }
-
-
when (text = @ss.scan(/[\s]*~[\s]*/))
-
action { [:TILDE, text] }
-
-
when (text = @ss.scan(/\:not\([\s]*/))
-
action { [:NOT, text] }
-
-
when (text = @ss.scan(/-?([0-9]+|[0-9]*\.[0-9]+)/))
-
action { [:NUMBER, text] }
-
-
when (text = @ss.scan(/[\s]*\/\/[\s]*/))
-
action { [:DOUBLESLASH, text] }
-
-
when (text = @ss.scan(/[\s]*\/[\s]*/))
-
action { [:SLASH, text] }
-
-
when (text = @ss.scan(/U\+[0-9a-f?]{1,6}(-[0-9a-f]{1,6})?/))
-
action {[:UNICODE_RANGE, text] }
-
-
when (text = @ss.scan(/[\s]+/))
-
action { [:S, text] }
-
-
when (text = @ss.scan(/"([^\n\r\f"]|\n|\r\n|\r|\f|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*"|'([^\n\r\f']|\n|\r\n|\r|\f|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*'/))
-
action { [:STRING, text] }
-
-
when (text = @ss.scan(/./))
-
action { [text, text] }
-
-
else
-
text = @ss.string[@ss.pos .. -1]
-
raise ScanError, "can not match: '" + text + "'"
-
end # if
-
-
else
-
raise ScanError, "undefined state: '" + state.to_s + "'"
-
end # case state
-
token
-
end # def _next_token
-
-
end # class
-
end
-
end
-
1
module Nokogiri
-
1
module CSS
-
1
class XPathVisitor # :nodoc:
-
1
def visit_function node
-
# note that nth-child and nth-last-child are preprocessed in css/node.rb.
-
msg = :"visit_function_#{node.value.first.gsub(/[(]/, '')}"
-
return self.send(msg, node) if self.respond_to?(msg)
-
-
case node.value.first
-
when /^text\(/
-
'child::text()'
-
when /^self\(/
-
"self::#{node.value[1]}"
-
when /^eq\(/
-
"position() = #{node.value[1]}"
-
when /^(nth|nth-of-type|nth-child)\(/
-
if node.value[1].is_a?(Nokogiri::CSS::Node) and node.value[1].type == :AN_PLUS_B
-
an_plus_b(node.value[1])
-
else
-
"position() = #{node.value[1]}"
-
end
-
when /^(nth-last-child|nth-last-of-type)\(/
-
if node.value[1].is_a?(Nokogiri::CSS::Node) and node.value[1].type == :AN_PLUS_B
-
an_plus_b(node.value[1], :last => true)
-
else
-
index = node.value[1].to_i - 1
-
index == 0 ? "position() = last()" : "position() = last() - #{index}"
-
end
-
when /^(first|first-of-type)\(/
-
"position() = 1"
-
when /^(last|last-of-type)\(/
-
"position() = last()"
-
when /^contains\(/
-
"contains(., #{node.value[1]})"
-
when /^gt\(/
-
"position() > #{node.value[1]}"
-
when /^only-child\(/
-
"last() = 1"
-
when /^comment\(/
-
"comment()"
-
when /^has\(/
-
node.value[1].accept(self)
-
else
-
args = ['.'] + node.value[1..-1]
-
"#{node.value.first}#{args.join(', ')})"
-
end
-
end
-
-
1
def visit_not node
-
child = node.value.first
-
if :ELEMENT_NAME == child.type
-
"not(self::#{child.accept(self)})"
-
else
-
"not(#{child.accept(self)})"
-
end
-
end
-
-
1
def visit_id node
-
node.value.first =~ /^#(.*)$/
-
"@id = '#{$1}'"
-
end
-
-
1
def visit_attribute_condition node
-
attribute = if (node.value.first.type == :FUNCTION) or (node.value.first.value.first =~ /::/)
-
''
-
else
-
'@'
-
end
-
attribute += node.value.first.accept(self)
-
-
# Support non-standard css
-
attribute.gsub!(/^@@/, '@')
-
-
return attribute unless node.value.length == 3
-
-
value = node.value.last
-
value = "'#{value}'" if value !~ /^['"]/
-
-
case node.value[1]
-
when :equal
-
attribute + " = " + "#{value}"
-
when :not_equal
-
attribute + " != " + "#{value}"
-
when :substring_match
-
"contains(#{attribute}, #{value})"
-
when :prefix_match
-
"starts-with(#{attribute}, #{value})"
-
when :dash_match
-
"#{attribute} = #{value} or starts-with(#{attribute}, concat(#{value}, '-'))"
-
when :includes
-
"contains(concat(\" \", #{attribute}, \" \"),concat(\" \", #{value}, \" \"))"
-
when :suffix_match
-
"substring(#{attribute}, string-length(#{attribute}) - " +
-
"string-length(#{value}) + 1, string-length(#{value})) = #{value}"
-
else
-
attribute + " #{node.value[1]} " + "#{value}"
-
end
-
end
-
-
1
def visit_pseudo_class node
-
if node.value.first.is_a?(Nokogiri::CSS::Node) and node.value.first.type == :FUNCTION
-
node.value.first.accept(self)
-
else
-
msg = :"visit_pseudo_class_#{node.value.first.gsub(/[(]/, '')}"
-
return self.send(msg, node) if self.respond_to?(msg)
-
-
case node.value.first
-
when "first", "first-child" then "position() = 1"
-
when "last", "last-child" then "position() = last()"
-
when "first-of-type" then "position() = 1"
-
when "last-of-type" then "position() = last()"
-
when "only-of-type" then "last() = 1"
-
when "empty" then "not(node())"
-
when "parent" then "node()"
-
when "root" then "not(parent::*)"
-
else
-
node.value.first + "(.)"
-
end
-
end
-
end
-
-
1
def visit_class_condition node
-
"contains(concat(' ', @class, ' '), ' #{node.value.first} ')"
-
end
-
-
{
-
'combinator' => ' and ',
-
'direct_adjacent_selector' => "/following-sibling::*[1]/self::",
-
'preceding_selector' => "/following-sibling::",
-
'descendant_selector' => '//',
-
'child_selector' => '/',
-
1
}.each do |k,v|
-
class_eval %{
-
def visit_#{k} node
-
"\#{node.value.first.accept(self)}#{v}\#{node.value.last.accept(self)}"
-
end
-
5
}
-
end
-
-
1
def visit_conditional_selector node
-
node.value.first.accept(self) + '[' +
-
node.value.last.accept(self) + ']'
-
end
-
-
1
def visit_element_name node
-
node.value.first
-
end
-
-
1
def accept node
-
node.accept(self)
-
end
-
-
1
private
-
1
def an_plus_b node, options={}
-
raise ArgumentError, "expected an+b node to contain 4 tokens, but is #{node.value.inspect}" unless node.value.size == 4
-
-
a = node.value[0].to_i
-
b = node.value[3].to_i
-
position = options[:last] ? "(last()-position()+1)" : "position()"
-
-
if (b == 0)
-
return "(#{position} mod #{a}) = 0"
-
else
-
compare = (a < 0) ? "<=" : ">="
-
return "(#{position} #{compare} #{b}) and (((#{position}-#{b}) mod #{a.abs}) = 0)"
-
end
-
end
-
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module Decorators
-
###
-
# The Slop decorator implements method missing such that a methods may be
-
# used instead of XPath or CSS. See Nokogiri.Slop
-
1
module Slop
-
###
-
# look for node with +name+. See Nokogiri.Slop
-
1
def method_missing name, *args, &block
-
prefix = implied_xpath_context
-
-
if args.empty?
-
list = xpath("#{prefix}#{name.to_s.sub(/^_/, '')}")
-
elsif args.first.is_a? Hash
-
hash = args.first
-
if hash[:css]
-
list = css("#{name}#{hash[:css]}")
-
elsif hash[:xpath]
-
conds = Array(hash[:xpath]).join(' and ')
-
list = xpath("#{prefix}#{name}[#{conds}]")
-
end
-
else
-
CSS::Parser.without_cache do
-
list = xpath(
-
*CSS.xpath_for("#{name}#{args.first}", :prefix => prefix)
-
)
-
end
-
end
-
-
super if list.empty?
-
list.length == 1 ? list.first : list
-
end
-
end
-
end
-
end
-
1
require 'nokogiri/html/entity_lookup'
-
1
require 'nokogiri/html/document'
-
1
require 'nokogiri/html/document_fragment'
-
1
require 'nokogiri/html/sax/parser_context'
-
1
require 'nokogiri/html/sax/parser'
-
1
require 'nokogiri/html/sax/push_parser'
-
1
require 'nokogiri/html/element_description'
-
1
require 'nokogiri/html/element_description_defaults'
-
-
1
module Nokogiri
-
1
class << self
-
###
-
# Parse HTML. Convenience method for Nokogiri::HTML::Document.parse
-
1
def HTML thing, url = nil, encoding = nil, options = XML::ParseOptions::DEFAULT_HTML, &block
-
Nokogiri::HTML::Document.parse(thing, url, encoding, options, &block)
-
end
-
end
-
-
1
module HTML
-
1
class << self
-
###
-
# Parse HTML. Convenience method for Nokogiri::HTML::Document.parse
-
1
def parse thing, url = nil, encoding = nil, options = XML::ParseOptions::DEFAULT_HTML, &block
-
Document.parse(thing, url, encoding, options, &block)
-
end
-
-
####
-
# Parse a fragment from +string+ in to a NodeSet.
-
1
def fragment string, encoding = nil
-
HTML::DocumentFragment.parse string, encoding
-
end
-
end
-
-
# Instance of Nokogiri::HTML::EntityLookup
-
1
NamedCharacters = EntityLookup.new
-
end
-
end
-
1
module Nokogiri
-
1
module HTML
-
###
-
# Nokogiri HTML builder is used for building HTML documents. It is very
-
# similar to the Nokogiri::XML::Builder. In fact, you should go read the
-
# documentation for Nokogiri::XML::Builder before reading this
-
# documentation.
-
#
-
# == Synopsis:
-
#
-
# Create an HTML document with a body that has an onload attribute, and a
-
# span tag with a class of "bold" that has content of "Hello world".
-
#
-
# builder = Nokogiri::HTML::Builder.new do |doc|
-
# doc.html {
-
# doc.body(:onload => 'some_func();') {
-
# doc.span.bold {
-
# doc.text "Hello world"
-
# }
-
# }
-
# }
-
# end
-
# puts builder.to_html
-
#
-
# The HTML builder inherits from the XML builder, so make sure to read the
-
# Nokogiri::XML::Builder documentation.
-
1
class Builder < Nokogiri::XML::Builder
-
###
-
# Convert the builder to HTML
-
1
def to_html
-
@doc.to_html
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module HTML
-
1
class Document < Nokogiri::XML::Document
-
###
-
# Get the meta tag encoding for this document. If there is no meta tag,
-
# then nil is returned.
-
1
def meta_encoding
-
meta = meta_content_type and
-
/charset\s*=\s*([\w-]+)/i.match(meta['content'])[1]
-
end
-
-
###
-
# Set the meta tag encoding for this document. If there is no meta
-
# content tag, the encoding is not set.
-
1
def meta_encoding= encoding
-
meta = meta_content_type and
-
meta['content'] = "text/html; charset=%s" % encoding
-
end
-
-
1
def meta_content_type
-
css('meta[@http-equiv]').find { |node|
-
node['http-equiv'] =~ /\AContent-Type\z/i and
-
!node['content'].nil? and
-
!node['content'].empty?
-
}
-
end
-
1
private :meta_content_type
-
-
###
-
# Get the title string of this document. Return nil if there is
-
# no title tag.
-
1
def title
-
title = at('title') and title.inner_text
-
end
-
-
###
-
# Set the title string of this document. If there is no head
-
# element, the title is not set.
-
1
def title=(text)
-
unless title = at('title')
-
head = at('head') or return nil
-
title = Nokogiri::XML::Node.new('title', self)
-
head << title
-
end
-
title.children = XML::Text.new(text, self)
-
end
-
-
####
-
# Serialize Node using +options+. Save options can also be set using a
-
# block. See SaveOptions.
-
#
-
# These two statements are equivalent:
-
#
-
# node.serialize(:encoding => 'UTF-8', :save_with => FORMAT | AS_XML)
-
#
-
# or
-
#
-
# node.serialize(:encoding => 'UTF-8') do |config|
-
# config.format.as_xml
-
# end
-
#
-
1
def serialize options = {}
-
options[:save_with] ||= XML::Node::SaveOptions::DEFAULT_HTML
-
super
-
end
-
-
####
-
# Create a Nokogiri::XML::DocumentFragment from +tags+
-
1
def fragment tags = nil
-
DocumentFragment.new(self, tags, self.root)
-
end
-
-
1
class << self
-
###
-
# Parse HTML. +string_or_io+ may be a String, or any object that
-
# responds to _read_ and _close_ such as an IO, or StringIO.
-
# +url+ is resource where this document is located. +encoding+ is the
-
# encoding that should be used when processing the document. +options+
-
# is a number that sets options in the parser, such as
-
# Nokogiri::XML::ParseOptions::RECOVER. See the constants in
-
# Nokogiri::XML::ParseOptions.
-
1
def parse string_or_io, url = nil, encoding = nil, options = XML::ParseOptions::DEFAULT_HTML
-
-
options = Nokogiri::XML::ParseOptions.new(options) if Fixnum === options
-
# Give the options to the user
-
yield options if block_given?
-
-
if string_or_io.respond_to?(:encoding)
-
unless string_or_io.encoding.name == "ASCII-8BIT"
-
encoding ||= string_or_io.encoding.name
-
end
-
end
-
-
if string_or_io.respond_to?(:read)
-
url ||= string_or_io.respond_to?(:path) ? string_or_io.path : nil
-
if !encoding
-
# Libxml2's parser has poor support for encoding
-
# detection. First, it does not recognize the HTML5
-
# style meta charset declaration. Secondly, even if it
-
# successfully detects an encoding hint, it does not
-
# re-decode or re-parse the preceding part which may be
-
# garbled.
-
#
-
# EncodingReader aims to perform advanced encoding
-
# detection beyond what Libxml2 does, and to emulate
-
# rewinding of a stream and make Libxml2 redo parsing
-
# from the start when an encoding hint is found.
-
string_or_io = EncodingReader.new(string_or_io)
-
begin
-
return read_io(string_or_io, url, encoding, options.to_i)
-
rescue EncodingFound => e
-
encoding = e.found_encoding
-
end
-
end
-
return read_io(string_or_io, url, encoding, options.to_i)
-
end
-
-
# read_memory pukes on empty docs
-
return new if string_or_io.nil? or string_or_io.empty?
-
-
encoding ||= EncodingReader.detect_encoding(string_or_io)
-
-
read_memory(string_or_io, url, encoding, options.to_i)
-
end
-
end
-
-
1
class EncodingFound < StandardError # :nodoc:
-
1
attr_reader :found_encoding
-
-
1
def initialize(encoding)
-
@found_encoding = encoding
-
super("encoding found: %s" % encoding)
-
end
-
end
-
-
1
class EncodingReader # :nodoc:
-
1
class SAXHandler < Nokogiri::XML::SAX::Document # :nodoc:
-
1
attr_reader :encoding
-
-
1
def initialize
-
@encoding = nil
-
super()
-
end
-
-
1
def start_element(name, attrs = [])
-
return unless name == 'meta'
-
attr = Hash[attrs]
-
charset = attr['charset'] and
-
@encoding = charset
-
http_equiv = attr['http-equiv'] and
-
http_equiv.match(/\AContent-Type\z/i) and
-
content = attr['content'] and
-
m = content.match(/;\s*charset\s*=\s*([\w-]+)/) and
-
@encoding = m[1]
-
end
-
end
-
-
1
class JumpSAXHandler < SAXHandler
-
1
def initialize(jumptag)
-
@jumptag = jumptag
-
super()
-
end
-
-
1
def start_element(name, attrs = [])
-
super
-
throw @jumptag, @encoding if @encoding
-
throw @jumptag, nil if name =~ /\A(?:div|h1|img|p|br)\z/
-
end
-
end
-
-
1
def self.detect_encoding(chunk)
-
if Nokogiri.jruby? && EncodingReader.is_jruby_without_fix?
-
return EncodingReader.detect_encoding_for_jruby_without_fix(chunk)
-
end
-
m = chunk.match(/\A(<\?xml[ \t\r\n]+[^>]*>)/) and
-
return Nokogiri.XML(m[1]).encoding
-
-
if Nokogiri.jruby?
-
m = chunk.match(/(<meta\s)(.*)(charset\s*=\s*([\w-]+))(.*)/i) and
-
return m[4]
-
catch(:encoding_found) {
-
Nokogiri::HTML::SAX::Parser.new(JumpSAXHandler.new(:encoding_found.to_s)).parse(chunk)
-
nil
-
}
-
else
-
handler = SAXHandler.new
-
parser = Nokogiri::HTML::SAX::PushParser.new(handler)
-
parser << chunk rescue Nokogiri::SyntaxError
-
handler.encoding
-
end
-
end
-
-
1
def self.is_jruby_without_fix?
-
JRUBY_VERSION.split('.').join.to_i < 165
-
end
-
-
1
def self.detect_encoding_for_jruby_without_fix(chunk)
-
m = chunk.match(/\A(<\?xml[ \t\r\n]+[^>]*>)/) and
-
return Nokogiri.XML(m[1]).encoding
-
-
m = chunk.match(/(<meta\s)(.*)(charset\s*=\s*([\w-]+))(.*)/i) and
-
return m[4]
-
-
catch(:encoding_found) {
-
Nokogiri::HTML::SAX::Parser.new(JumpSAXHandler.new(:encoding_found.to_s)).parse(chunk)
-
nil
-
}
-
rescue Nokogiri::SyntaxError, RuntimeError
-
# Ignore parser errors that nokogiri may raise
-
nil
-
end
-
-
1
def initialize(io)
-
@io = io
-
@firstchunk = nil
-
@encoding_found = nil
-
end
-
-
# This method is used by the C extension so that
-
# Nokogiri::HTML::Document#read_io() does not leak memory when
-
# EncodingFound is raised.
-
1
attr_reader :encoding_found
-
-
1
def read(len)
-
# no support for a call without len
-
-
if !@firstchunk
-
@firstchunk = @io.read(len) or return nil
-
-
# This implementation expects that the first call from
-
# htmlReadIO() is made with a length long enough (~1KB) to
-
# achieve advanced encoding detection.
-
if encoding = EncodingReader.detect_encoding(@firstchunk)
-
# The first chunk is stored for the next read in retry.
-
raise @encoding_found = EncodingFound.new(encoding)
-
end
-
end
-
@encoding_found = nil
-
-
ret = @firstchunk.slice!(0, len)
-
if (len -= ret.length) > 0
-
rest = @io.read(len) and ret << rest
-
end
-
if ret.empty?
-
nil
-
else
-
ret
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module HTML
-
1
class DocumentFragment < Nokogiri::XML::DocumentFragment
-
1
attr_accessor :errors
-
-
####
-
# Create a Nokogiri::XML::DocumentFragment from +tags+, using +encoding+
-
1
def self.parse tags, encoding = nil
-
doc = HTML::Document.new
-
-
encoding ||= tags.respond_to?(:encoding) ? tags.encoding.name : 'UTF-8'
-
doc.encoding = encoding
-
-
new(doc, tags)
-
end
-
-
1
def initialize document, tags = nil, ctx = nil
-
return self unless tags
-
-
if ctx
-
preexisting_errors = document.errors.dup
-
node_set = ctx.parse("<div>#{tags}</div>")
-
node_set.first.children.each { |child| child.parent = self } unless node_set.empty?
-
self.errors = document.errors - preexisting_errors
-
else
-
# This is a horrible hack, but I don't care
-
if tags.strip =~ /^<body/i
-
path = "/html/body"
-
else
-
path = "/html/body/node()"
-
end
-
-
temp_doc = HTML::Document.parse "<html><body>#{tags}", nil, document.encoding
-
temp_doc.xpath(path).each { |child| child.parent = self }
-
self.errors = temp_doc.errors
-
end
-
children
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module HTML
-
1
class ElementDescription
-
###
-
# Is this element a block element?
-
1
def block?
-
!inline?
-
end
-
-
###
-
# Convert this description to a string
-
1
def to_s
-
"#{name}: #{description}"
-
end
-
-
###
-
# Inspection information
-
1
def inspect
-
"#<#{self.class.name}: #{name} #{description}>"
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module HTML
-
1
class ElementDescription
-
-
# Methods are defined protected by method_defined? because at
-
# this point the C-library or Java library is alraedy loaded,
-
# and we don't want to clobber any methods that have been
-
# defined there.
-
-
1
Desc = Struct.new("HTMLElementDescription", :name,
-
:startTag, :endTag, :saveEndTag,
-
:empty, :depr, :dtd, :isinline,
-
:desc,
-
:subelts, :defaultsubelt,
-
:attrs_opt, :attrs_depr, :attrs_req)
-
-
# This is filled in down below.
-
1
DefaultDescriptions = Hash.new()
-
-
1
def default_desc
-
DefaultDescriptions[name.downcase]
-
end
-
1
private :default_desc
-
-
1
unless method_defined? :implied_start_tag?
-
def implied_start_tag?
-
d = default_desc
-
d ? d.startTag : nil
-
end
-
end
-
-
1
unless method_defined? :implied_end_tag?
-
def implied_end_tag?
-
d = default_desc
-
d ? d.endTag : nil
-
end
-
end
-
-
1
unless method_defined? :save_end_tag?
-
def save_end_tag?
-
d = default_desc
-
d ? d.saveEndTag : nil
-
end
-
end
-
-
1
unless method_defined? :deprecated?
-
def deprecated?
-
d = default_desc
-
d ? d.depr : nil
-
end
-
end
-
-
1
unless method_defined? :description
-
def description
-
d = default_desc
-
d ? d.desc : nil
-
end
-
end
-
-
1
unless method_defined? :default_sub_element
-
def default_sub_element
-
d = default_desc
-
d ? d.defaultsubelt : nil
-
end
-
end
-
-
1
unless method_defined? :optional_attributes
-
def optional_attributes
-
d = default_desc
-
d ? d.attrs_opt : []
-
end
-
end
-
-
1
unless method_defined? :deprecated_attributes
-
def deprecated_attributes
-
d = default_desc
-
d ? d.attrs_depr : []
-
end
-
end
-
-
1
unless method_defined? :required_attributes
-
def required_attributes
-
d = default_desc
-
d ? d.attrs_req : []
-
end
-
end
-
-
###
-
# Default Element Descriptions (HTML 4.0) copied from
-
# libxml2/HTMLparser.c and libxml2/include/libxml/HTMLparser.h
-
#
-
# The copyright notice for those files and the following list of
-
# element and attribute descriptions is reproduced here:
-
#
-
# Except where otherwise noted in the source code (e.g. the
-
# files hash.c, list.c and the trio files, which are covered by
-
# a similar licence but with different Copyright notices) all
-
# the files are:
-
#
-
# Copyright (C) 1998-2003 Daniel Veillard. All Rights Reserved.
-
#
-
# Permission is hereby granted, free of charge, to any person
-
# obtaining a copy of this software and associated documentation
-
# files (the "Software"), to deal in the Software without
-
# restriction, including without limitation the rights to use,
-
# copy, modify, merge, publish, distribute, sublicense, and/or
-
# sell copies of the Software, and to permit persons to whom the
-
# Software is fur- nished to do so, subject to the following
-
# conditions:
-
-
# The above copyright notice and this permission notice shall be
-
# included in all copies or substantial portions of the
-
# Software.
-
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
-
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
-
# WARRANTIES OF MERCHANTABILITY, FIT- NESS FOR A PARTICULAR
-
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE DANIEL
-
# VEILLARD BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-
# FROM, OUT OF OR IN CON- NECTION WITH THE SOFTWARE OR THE USE
-
# OR OTHER DEALINGS IN THE SOFTWARE.
-
-
# Except as contained in this notice, the name of Daniel
-
# Veillard shall not be used in advertising or otherwise to
-
# promote the sale, use or other deal- ings in this Software
-
# without prior written authorization from him.
-
-
# Attributes defined and categorized
-
1
FONTSTYLE = ["tt", "i", "b", "u", "s", "strike", "big", "small"]
-
1
PHRASE = ['em', 'strong', 'dfn', 'code', 'samp',
-
'kbd', 'var', 'cite', 'abbr', 'acronym']
-
1
SPECIAL = ['a', 'img', 'applet', 'embed', 'object', 'font','basefont',
-
'br', 'script', 'map', 'q', 'sub', 'sup', 'span', 'bdo',
-
'iframe']
-
1
PCDATA = []
-
1
HEADING = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']
-
1
LIST = ['ul', 'ol', 'dir', 'menu']
-
1
FORMCTRL = ['input', 'select', 'textarea', 'label', 'button']
-
1
BLOCK = [HEADING, LIST, 'pre', 'p', 'dl', 'div', 'center', 'noscript',
-
'noframes', 'blockquote', 'form', 'isindex', 'hr', 'table',
-
'fieldset', 'address']
-
1
INLINE = [PCDATA, FONTSTYLE, PHRASE, SPECIAL, FORMCTRL]
-
1
FLOW = [BLOCK, INLINE]
-
1
MODIFIER = []
-
1
EMPTY = []
-
-
1
HTML_FLOW = FLOW
-
1
HTML_INLINE = INLINE
-
1
HTML_PCDATA = PCDATA
-
1
HTML_CDATA = HTML_PCDATA
-
-
1
COREATTRS = ['id', 'class', 'style', 'title']
-
1
I18N = ['lang', 'dir']
-
1
EVENTS = ['onclick', 'ondblclick', 'onmousedown', 'onmouseup',
-
'onmouseover', 'onmouseout', 'onkeypress', 'onkeydown',
-
'onkeyup']
-
1
ATTRS = [COREATTRS, I18N,EVENTS]
-
1
CELLHALIGN = ['align', 'char', 'charoff']
-
1
CELLVALIGN = ['valign']
-
-
1
HTML_ATTRS = ATTRS
-
1
CORE_I18N_ATTRS = [COREATTRS, I18N]
-
1
CORE_ATTRS = COREATTRS
-
1
I18N_ATTRS = I18N
-
-
-
1
A_ATTRS = [ATTRS, 'charset', 'type', 'name',
-
'href', 'hreflang', 'rel', 'rev', 'accesskey', 'shape',
-
'coords', 'tabindex', 'onfocus', 'onblur']
-
1
TARGET_ATTR = ['target']
-
1
ROWS_COLS_ATTR = ['rows', 'cols']
-
1
ALT_ATTR = ['alt']
-
1
SRC_ALT_ATTRS = ['src', 'alt']
-
1
HREF_ATTRS = ['href']
-
1
CLEAR_ATTRS = ['clear']
-
1
INLINE_P = [INLINE, 'p']
-
-
1
FLOW_PARAM = [FLOW, 'param']
-
1
APPLET_ATTRS = [COREATTRS , 'codebase',
-
'archive', 'alt', 'name', 'height', 'width', 'align',
-
'hspace', 'vspace']
-
1
AREA_ATTRS = ['shape', 'coords', 'href', 'nohref',
-
'tabindex', 'accesskey', 'onfocus', 'onblur']
-
1
BASEFONT_ATTRS = ['id', 'size', 'color', 'face']
-
1
QUOTE_ATTRS = [ATTRS, 'cite']
-
1
BODY_CONTENTS = [FLOW, 'ins', 'del']
-
1
BODY_ATTRS = [ATTRS, 'onload', 'onunload']
-
1
BODY_DEPR = ['background', 'bgcolor', 'text',
-
'link', 'vlink', 'alink']
-
1
BUTTON_ATTRS = [ATTRS, 'name', 'value', 'type',
-
'disabled', 'tabindex', 'accesskey', 'onfocus', 'onblur']
-
-
-
1
COL_ATTRS = [ATTRS, 'span', 'width', CELLHALIGN, CELLVALIGN]
-
1
COL_ELT = ['col']
-
1
EDIT_ATTRS = [ATTRS, 'datetime', 'cite']
-
1
COMPACT_ATTRS = [ATTRS, 'compact']
-
1
DL_CONTENTS = ['dt', 'dd']
-
1
COMPACT_ATTR = ['compact']
-
1
LABEL_ATTR = ['label']
-
1
FIELDSET_CONTENTS = [FLOW, 'legend' ]
-
1
FONT_ATTRS = [COREATTRS, I18N, 'size', 'color', 'face' ]
-
1
FORM_CONTENTS = [HEADING, LIST, INLINE, 'pre', 'p', 'div', 'center',
-
'noscript', 'noframes', 'blockquote', 'isindex', 'hr',
-
'table', 'fieldset', 'address']
-
1
FORM_ATTRS = [ATTRS, 'method', 'enctype', 'accept', 'name', 'onsubmit',
-
'onreset', 'accept-charset']
-
1
FRAME_ATTRS = [COREATTRS, 'longdesc', 'name', 'src', 'frameborder',
-
'marginwidth', 'marginheight', 'noresize', 'scrolling' ]
-
1
FRAMESET_ATTRS = [COREATTRS, 'rows', 'cols', 'onload', 'onunload']
-
1
FRAMESET_CONTENTS = ['frameset', 'frame', 'noframes']
-
1
HEAD_ATTRS = [I18N, 'profile']
-
1
HEAD_CONTENTS = ['title', 'isindex', 'base', 'script', 'style', 'meta',
-
'link', 'object']
-
1
HR_DEPR = ['align', 'noshade', 'size', 'width']
-
1
VERSION_ATTR = ['version']
-
1
HTML_CONTENT = ['head', 'body', 'frameset']
-
1
IFRAME_ATTRS = [COREATTRS, 'longdesc', 'name', 'src', 'frameborder',
-
'marginwidth', 'marginheight', 'scrolling', 'align',
-
'height', 'width']
-
1
IMG_ATTRS = [ATTRS, 'longdesc', 'name', 'height', 'width', 'usemap',
-
'ismap']
-
1
EMBED_ATTRS = [COREATTRS, 'align', 'alt', 'border', 'code', 'codebase',
-
'frameborder', 'height', 'hidden', 'hspace', 'name',
-
'palette', 'pluginspace', 'pluginurl', 'src', 'type',
-
'units', 'vspace', 'width']
-
1
INPUT_ATTRS = [ATTRS, 'type', 'name', 'value', 'checked', 'disabled',
-
'readonly', 'size', 'maxlength', 'src', 'alt', 'usemap',
-
'ismap', 'tabindex', 'accesskey', 'onfocus', 'onblur',
-
'onselect', 'onchange', 'accept']
-
1
PROMPT_ATTRS = [COREATTRS, I18N, 'prompt']
-
1
LABEL_ATTRS = [ATTRS, 'for', 'accesskey', 'onfocus', 'onblur']
-
1
LEGEND_ATTRS = [ATTRS, 'accesskey']
-
1
ALIGN_ATTR = ['align']
-
1
LINK_ATTRS = [ATTRS, 'charset', 'href', 'hreflang', 'type', 'rel', 'rev',
-
'media']
-
1
MAP_CONTENTS = [BLOCK, 'area']
-
1
NAME_ATTR = ['name']
-
1
ACTION_ATTR = ['action']
-
1
BLOCKLI_ELT = [BLOCK, 'li']
-
1
META_ATTRS = [I18N, 'http-equiv', 'name', 'scheme']
-
1
CONTENT_ATTR = ['content']
-
1
TYPE_ATTR = ['type']
-
1
NOFRAMES_CONTENT = ['body', FLOW, MODIFIER]
-
1
OBJECT_CONTENTS = [FLOW, 'param']
-
1
OBJECT_ATTRS = [ATTRS, 'declare', 'classid', 'codebase', 'data', 'type',
-
'codetype', 'archive', 'standby', 'height', 'width',
-
'usemap', 'name', 'tabindex']
-
1
OBJECT_DEPR = ['align', 'border', 'hspace', 'vspace']
-
1
OL_ATTRS = ['type', 'compact', 'start']
-
1
OPTION_ELT = ['option']
-
1
OPTGROUP_ATTRS = [ATTRS, 'disabled']
-
1
OPTION_ATTRS = [ATTRS, 'disabled', 'label', 'selected', 'value']
-
1
PARAM_ATTRS = ['id', 'value', 'valuetype', 'type']
-
1
WIDTH_ATTR = ['width']
-
1
PRE_CONTENT = [PHRASE, 'tt', 'i', 'b', 'u', 's', 'strike', 'a', 'br',
-
'script', 'map', 'q', 'span', 'bdo', 'iframe']
-
1
SCRIPT_ATTRS = ['charset', 'src', 'defer', 'event', 'for']
-
1
LANGUAGE_ATTR = ['language']
-
1
SELECT_CONTENT = ['optgroup', 'option']
-
1
SELECT_ATTRS = [ATTRS, 'name', 'size', 'multiple', 'disabled', 'tabindex',
-
'onfocus', 'onblur', 'onchange']
-
1
STYLE_ATTRS = [I18N, 'media', 'title']
-
1
TABLE_ATTRS = [ATTRS, 'summary', 'width', 'border', 'frame', 'rules',
-
'cellspacing', 'cellpadding', 'datapagesize']
-
1
TABLE_DEPR = ['align', 'bgcolor']
-
1
TABLE_CONTENTS = ['caption', 'col', 'colgroup', 'thead', 'tfoot', 'tbody',
-
'tr']
-
1
TR_ELT = ['tr']
-
1
TALIGN_ATTRS = [ATTRS, CELLHALIGN, CELLVALIGN]
-
1
TH_TD_DEPR = ['nowrap', 'bgcolor', 'width', 'height']
-
1
TH_TD_ATTR = [ATTRS, 'abbr', 'axis', 'headers', 'scope', 'rowspan',
-
'colspan', CELLHALIGN, CELLVALIGN]
-
1
TEXTAREA_ATTRS = [ATTRS, 'name', 'disabled', 'readonly', 'tabindex',
-
'accesskey', 'onfocus', 'onblur', 'onselect',
-
'onchange']
-
1
TR_CONTENTS = ['th', 'td']
-
1
BGCOLOR_ATTR = ['bgcolor']
-
1
LI_ELT = ['li']
-
1
UL_DEPR = ['type', 'compact']
-
1
DIR_ATTR = ['dir']
-
-
[
-
['a', false, false, false, false, false, :any, true,
-
'anchor ',
-
HTML_INLINE, nil, A_ATTRS, TARGET_ATTR, []
-
1
],
-
['abbr', false, false, false, false, false, :any, true,
-
'abbreviated form',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['acronym', false, false, false, false, false, :any, true, '',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['address', false, false, false, false, false, :any, false,
-
'information on author',
-
INLINE_P , nil, HTML_ATTRS, [], []
-
],
-
['applet', false, false, false, false, true, :loose, true,
-
'java applet ',
-
FLOW_PARAM, nil, [], APPLET_ATTRS, []
-
],
-
['area', false, true, true, true, false, :any, false,
-
'client-side image map area ',
-
EMPTY, nil, AREA_ATTRS, TARGET_ATTR, ALT_ATTR
-
],
-
['b', false, true, false, false, false, :any, true,
-
'bold text style',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['base', false, true, true, true, false, :any, false,
-
'document base uri ',
-
EMPTY, nil, [], TARGET_ATTR, HREF_ATTRS
-
],
-
['basefont', false, true, true, true, true, :loose, true,
-
'base font size ',
-
EMPTY, nil, [], BASEFONT_ATTRS, []
-
],
-
['bdo', false, false, false, false, false, :any, true,
-
'i18n bidi over-ride ',
-
HTML_INLINE, nil, CORE_I18N_ATTRS, [], DIR_ATTR
-
],
-
['big', false, true, false, false, false, :any, true,
-
'large text style',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['blockquote', false, false, false, false, false, :any, false,
-
'long quotation ',
-
HTML_FLOW, nil, QUOTE_ATTRS, [], []
-
],
-
['body', true, true, false, false, false, :any, false,
-
'document body ',
-
BODY_CONTENTS, 'div', BODY_ATTRS, BODY_DEPR, []
-
],
-
['br', false, true, true, true, false, :any, true,
-
'forced line break ',
-
EMPTY, nil, CORE_ATTRS, CLEAR_ATTRS, []
-
],
-
['button', false, false, false, false, false, :any, true,
-
'push button ',
-
[HTML_FLOW, MODIFIER], nil, BUTTON_ATTRS, [], []
-
],
-
['caption', false, false, false, false, false, :any, false,
-
'table caption ',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['center', false, true, false, false, true, :loose, false,
-
'shorthand for div align=center ',
-
HTML_FLOW, nil, [], HTML_ATTRS, []
-
],
-
['cite', false, false, false, false, false, :any, true, 'citation',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['code', false, false, false, false, false, :any, true,
-
'computer code fragment',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['col', false, true, true, true, false, :any, false, 'table column ',
-
EMPTY, nil, COL_ATTRS, [], []
-
],
-
['colgroup', false, true, false, false, false, :any, false,
-
'table column group ',
-
COL_ELT, 'col', COL_ATTRS, [], []
-
],
-
['dd', false, true, false, false, false, :any, false,
-
'definition description ',
-
HTML_FLOW, nil, HTML_ATTRS, [], []
-
],
-
['del', false, false, false, false, false, :any, true,
-
'deleted text ',
-
HTML_FLOW, nil, EDIT_ATTRS, [], []
-
],
-
['dfn', false, false, false, false, false, :any, true,
-
'instance definition',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['dir', false, false, false, false, true, :loose, false,
-
'directory list',
-
BLOCKLI_ELT, 'li', [], COMPACT_ATTRS, []
-
],
-
['div', false, false, false, false, false, :any, false,
-
'generic language/style container',
-
HTML_FLOW, nil, HTML_ATTRS, ALIGN_ATTR, []
-
],
-
['dl', false, false, false, false, false, :any, false,
-
'definition list ',
-
DL_CONTENTS, 'dd', HTML_ATTRS, COMPACT_ATTR, []
-
],
-
['dt', false, true, false, false, false, :any, false,
-
'definition term ',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['em', false, true, false, false, false, :any, true,
-
'emphasis',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['embed', false, true, false, false, true, :loose, true,
-
'generic embedded object ',
-
EMPTY, nil, EMBED_ATTRS, [], []
-
],
-
['fieldset', false, false, false, false, false, :any, false,
-
'form control group ',
-
FIELDSET_CONTENTS, nil, HTML_ATTRS, [], []
-
],
-
['font', false, true, false, false, true, :loose, true,
-
'local change to font ',
-
HTML_INLINE, nil, [], FONT_ATTRS, []
-
],
-
['form', false, false, false, false, false, :any, false,
-
'interactive form ',
-
FORM_CONTENTS, 'fieldset', FORM_ATTRS, TARGET_ATTR, ACTION_ATTR
-
],
-
['frame', false, true, true, true, false, :frameset, false,
-
'subwindow ',
-
EMPTY, nil, [], FRAME_ATTRS, []
-
],
-
['frameset', false, false, false, false, false, :frameset, false,
-
'window subdivision',
-
FRAMESET_CONTENTS, 'noframes', [], FRAMESET_ATTRS, []
-
],
-
['htrue', false, false, false, false, false, :any, false,
-
'heading ',
-
HTML_INLINE, nil, HTML_ATTRS, ALIGN_ATTR, []
-
],
-
['htrue', false, false, false, false, false, :any, false,
-
'heading ',
-
HTML_INLINE, nil, HTML_ATTRS, ALIGN_ATTR, []
-
],
-
['htrue', false, false, false, false, false, :any, false,
-
'heading ',
-
HTML_INLINE, nil, HTML_ATTRS, ALIGN_ATTR, []
-
],
-
['h4', false, false, false, false, false, :any, false,
-
'heading ',
-
HTML_INLINE, nil, HTML_ATTRS, ALIGN_ATTR, []
-
],
-
['h5', false, false, false, false, false, :any, false,
-
'heading ',
-
HTML_INLINE, nil, HTML_ATTRS, ALIGN_ATTR, []
-
],
-
['h6', false, false, false, false, false, :any, false,
-
'heading ',
-
HTML_INLINE, nil, HTML_ATTRS, ALIGN_ATTR, []
-
],
-
['head', true, true, false, false, false, :any, false,
-
'document head ',
-
HEAD_CONTENTS, nil, HEAD_ATTRS, [], []
-
],
-
['hr', false, true, true, true, false, :any, false,
-
'horizontal rule ',
-
EMPTY, nil, HTML_ATTRS, HR_DEPR, []
-
],
-
['html', true, true, false, false, false, :any, false,
-
'document root element ',
-
HTML_CONTENT, nil, I18N_ATTRS, VERSION_ATTR, []
-
],
-
['i', false, true, false, false, false, :any, true,
-
'italic text style',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['iframe', false, false, false, false, false, :any, true,
-
'inline subwindow ',
-
HTML_FLOW, nil, [], IFRAME_ATTRS, []
-
],
-
['img', false, true, true, true, false, :any, true,
-
'embedded image ',
-
EMPTY, nil, IMG_ATTRS, ALIGN_ATTR, SRC_ALT_ATTRS
-
],
-
['input', false, true, true, true, false, :any, true,
-
'form control ',
-
EMPTY, nil, INPUT_ATTRS, ALIGN_ATTR, []
-
],
-
['ins', false, false, false, false, false, :any, true,
-
'inserted text',
-
HTML_FLOW, nil, EDIT_ATTRS, [], []
-
],
-
['isindex', false, true, true, true, true, :loose, false,
-
'single line prompt ',
-
EMPTY, nil, [], PROMPT_ATTRS, []
-
],
-
['kbd', false, false, false, false, false, :any, true,
-
'text to be entered by the user',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['label', false, false, false, false, false, :any, true,
-
'form field label text ',
-
[HTML_INLINE, MODIFIER], nil, LABEL_ATTRS, [], []
-
],
-
['legend', false, false, false, false, false, :any, false,
-
'fieldset legend ',
-
HTML_INLINE, nil, LEGEND_ATTRS, ALIGN_ATTR, []
-
],
-
['li', false, true, true, false, false, :any, false,
-
'list item ',
-
HTML_FLOW, nil, HTML_ATTRS, [], []
-
],
-
['link', false, true, true, true, false, :any, false,
-
'a media-independent link ',
-
EMPTY, nil, LINK_ATTRS, TARGET_ATTR, []
-
],
-
['map', false, false, false, false, false, :any, true,
-
'client-side image map ',
-
MAP_CONTENTS, nil, HTML_ATTRS, [], NAME_ATTR
-
],
-
['menu', false, false, false, false, true, :loose, false,
-
'menu list ',
-
BLOCKLI_ELT, nil, [], COMPACT_ATTRS, []
-
],
-
['meta', false, true, true, true, false, :any, false,
-
'generic metainformation ',
-
EMPTY, nil, META_ATTRS, [], CONTENT_ATTR
-
],
-
['noframes', false, false, false, false, false, :frameset, false,
-
'alternate content container for non frame-based rendering ',
-
NOFRAMES_CONTENT, 'body', HTML_ATTRS, [], []
-
],
-
['noscript', false, false, false, false, false, :any, false,
-
'alternate content container for non script-based rendering ',
-
HTML_FLOW, 'div', HTML_ATTRS, [], []
-
],
-
['object', false, false, false, false, false, :any, true,
-
'generic embedded object ',
-
OBJECT_CONTENTS, 'div', OBJECT_ATTRS, OBJECT_DEPR, []
-
],
-
['ol', false, false, false, false, false, :any, false,
-
'ordered list ',
-
LI_ELT, 'li', HTML_ATTRS, OL_ATTRS, []
-
],
-
['optgroup', false, false, false, false, false, :any, false,
-
'option group ',
-
OPTION_ELT, 'option', OPTGROUP_ATTRS, [], LABEL_ATTR
-
],
-
['option', false, true, false, false, false, :any, false,
-
'selectable choice ',
-
HTML_PCDATA, nil, OPTION_ATTRS, [], []
-
],
-
['p', false, true, false, false, false, :any, false,
-
'paragraph ',
-
HTML_INLINE, nil, HTML_ATTRS, ALIGN_ATTR, []
-
],
-
['param', false, true, true, true, false, :any, false,
-
'named property value ',
-
EMPTY, nil, PARAM_ATTRS, [], NAME_ATTR
-
],
-
['pre', false, false, false, false, false, :any, false,
-
'preformatted text ',
-
PRE_CONTENT, nil, HTML_ATTRS, WIDTH_ATTR, []
-
],
-
['q', false, false, false, false, false, :any, true,
-
'short inline quotation ',
-
HTML_INLINE, nil, QUOTE_ATTRS, [], []
-
],
-
['s', false, true, false, false, true, :loose, true,
-
'strike-through text style',
-
HTML_INLINE, nil, [], HTML_ATTRS, []
-
],
-
['samp', false, false, false, false, false, :any, true,
-
'sample program output, scripts, etc.',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['script', false, false, false, false, false, :any, true,
-
'script statements ',
-
HTML_CDATA, nil, SCRIPT_ATTRS, LANGUAGE_ATTR, TYPE_ATTR
-
],
-
['select', false, false, false, false, false, :any, true,
-
'option selector ',
-
SELECT_CONTENT, nil, SELECT_ATTRS, [], []
-
],
-
['small', false, true, false, false, false, :any, true,
-
'small text style',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['span', false, false, false, false, false, :any, true,
-
'generic language/style container ',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['strike', false, true, false, false, true, :loose, true,
-
'strike-through text',
-
HTML_INLINE, nil, [], HTML_ATTRS, []
-
],
-
['strong', false, true, false, false, false, :any, true,
-
'strong emphasis',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['style', false, false, false, false, false, :any, false,
-
'style info ',
-
HTML_CDATA, nil, STYLE_ATTRS, [], TYPE_ATTR
-
],
-
['sub', false, true, false, false, false, :any, true,
-
'subscript',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['sup', false, true, false, false, false, :any, true,
-
'superscript ',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['table', false, false, false, false, false, :any, false,
-
'',
-
TABLE_CONTENTS, 'tr', TABLE_ATTRS, TABLE_DEPR, []
-
],
-
['tbody', true, false, false, false, false, :any, false,
-
'table body ',
-
TR_ELT, 'tr', TALIGN_ATTRS, [], []
-
],
-
['td', false, false, false, false, false, :any, false,
-
'table data cell',
-
HTML_FLOW, nil, TH_TD_ATTR, TH_TD_DEPR, []
-
],
-
['textarea', false, false, false, false, false, :any, true,
-
'multi-line text field ',
-
HTML_PCDATA, nil, TEXTAREA_ATTRS, [], ROWS_COLS_ATTR
-
],
-
['tfoot', false, true, false, false, false, :any, false,
-
'table footer ',
-
TR_ELT, 'tr', TALIGN_ATTRS, [], []
-
],
-
['th', false, true, false, false, false, :any, false,
-
'table header cell',
-
HTML_FLOW, nil, TH_TD_ATTR, TH_TD_DEPR, []
-
],
-
['thead', false, true, false, false, false, :any, false,
-
'table header ',
-
TR_ELT, 'tr', TALIGN_ATTRS, [], []
-
],
-
['title', false, false, false, false, false, :any, false,
-
'document title ',
-
HTML_PCDATA, nil, I18N_ATTRS, [], []
-
],
-
['tr', false, false, false, false, false, :any, false,
-
'table row ',
-
TR_CONTENTS, 'td', TALIGN_ATTRS, BGCOLOR_ATTR, []
-
],
-
['tt', false, true, false, false, false, :any, true,
-
'teletype or monospaced text style',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['u', false, true, false, false, true, :loose, true,
-
'underlined text style',
-
HTML_INLINE, nil, [], HTML_ATTRS, []
-
],
-
['ul', false, false, false, false, false, :any, false,
-
'unordered list ',
-
LI_ELT, 'li', HTML_ATTRS, UL_DEPR, []
-
],
-
['var', false, false, false, false, false, :any, true,
-
'instance of a variable or program argument',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
]
-
].each do |descriptor|
-
92
name = descriptor[0]
-
-
92
begin
-
92
d = Desc.new(*descriptor)
-
-
# flatten all the attribute lists (Ruby1.9, *[a,b,c] can be
-
# used to flatten a literal list, but not in Ruby1.8).
-
92
d[:subelts] = d[:subelts].flatten
-
92
d[:attrs_opt] = d[:attrs_opt].flatten
-
92
d[:attrs_depr] = d[:attrs_depr].flatten
-
92
d[:attrs_req] = d[:attrs_req].flatten
-
rescue => e
-
p name
-
raise e
-
end
-
-
92
DefaultDescriptions[name] = d
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module HTML
-
1
class EntityDescription < Struct.new(:value, :name, :description); end
-
-
1
class EntityLookup
-
###
-
# Look up entity with +name+
-
1
def [] name
-
(val = get(name)) && val.value
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module HTML
-
###
-
# Nokogiri lets you write a SAX parser to process HTML but get HTML
-
# correction features.
-
#
-
# See Nokogiri::HTML::SAX::Parser for a basic example of using a
-
# SAX parser with HTML.
-
#
-
# For more information on SAX parsers, see Nokogiri::XML::SAX
-
1
module SAX
-
###
-
# This class lets you perform SAX style parsing on HTML with HTML
-
# error correction.
-
#
-
# Here is a basic usage example:
-
#
-
# class MyDoc < Nokogiri::XML::SAX::Document
-
# def start_element name, attributes = []
-
# puts "found a #{name}"
-
# end
-
# end
-
#
-
# parser = Nokogiri::HTML::SAX::Parser.new(MyDoc.new)
-
# parser.parse(File.read(ARGV[0], 'rb'))
-
#
-
# For more information on SAX parsers, see Nokogiri::XML::SAX
-
1
class Parser < Nokogiri::XML::SAX::Parser
-
###
-
# Parse html stored in +data+ using +encoding+
-
1
def parse_memory data, encoding = 'UTF-8'
-
raise ArgumentError unless data
-
return unless data.length > 0
-
ctx = ParserContext.memory(data, encoding)
-
yield ctx if block_given?
-
ctx.parse_with self
-
end
-
-
###
-
# Parse a file with +filename+
-
1
def parse_file filename, encoding = 'UTF-8'
-
raise ArgumentError unless filename
-
raise Errno::ENOENT unless File.exists?(filename)
-
raise Errno::EISDIR if File.directory?(filename)
-
ctx = ParserContext.file(filename, encoding)
-
yield ctx if block_given?
-
ctx.parse_with self
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module HTML
-
1
module SAX
-
###
-
# Context for HTML SAX parsers. This class is usually not instantiated
-
# by the user. Instead, you should be looking at
-
# Nokogiri::HTML::SAX::Parser
-
1
class ParserContext < Nokogiri::XML::SAX::ParserContext
-
1
def self.new thing, encoding = 'UTF-8'
-
[:read, :close].all? { |x| thing.respond_to?(x) } ? super :
-
memory(thing, encoding)
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module HTML
-
1
module SAX
-
1
class PushParser
-
1
def initialize(doc = XML::SAX::Document.new, file_name = nil, encoding = 'UTF-8')
-
@document = doc
-
@encoding = encoding
-
@sax_parser = HTML::SAX::Parser.new(doc, @encoding)
-
-
## Create our push parser context
-
initialize_native(@sax_parser, file_name, @encoding)
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
class SyntaxError < ::StandardError
-
end
-
end
-
1
module Nokogiri
-
# The version of Nokogiri you are using
-
1
VERSION = '1.5.2'
-
-
1
class VersionInfo # :nodoc:
-
1
def jruby?
-
2
::JRUBY_VERSION if RUBY_PLATFORM == "java"
-
end
-
-
1
def engine
-
1
defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'mri'
-
end
-
-
1
def loaded_parser_version
-
3
LIBXML_PARSER_VERSION.scan(/^(.*)(..)(..)$/).first.collect{ |j|
-
9
j.to_i
-
}.join(".")
-
end
-
-
1
def compiled_parser_version
-
3
LIBXML_VERSION
-
end
-
-
1
def libxml2?
-
3
defined?(LIBXML_VERSION)
-
end
-
-
1
def warnings
-
2
return [] unless libxml2?
-
-
2
if compiled_parser_version != loaded_parser_version
-
["Nokogiri was built against LibXML version #{compiled_parser_version}, but has dynamically loaded #{loaded_parser_version}"]
-
else
-
2
[]
-
end
-
end
-
-
1
def to_hash
-
1
hash_info = {}
-
1
hash_info['warnings'] = []
-
1
hash_info['nokogiri'] = Nokogiri::VERSION
-
1
hash_info['ruby'] = {}
-
1
hash_info['ruby']['version'] = ::RUBY_VERSION
-
1
hash_info['ruby']['platform'] = ::RUBY_PLATFORM
-
1
hash_info['ruby']['description'] = ::RUBY_DESCRIPTION
-
1
hash_info['ruby']['engine'] = engine
-
1
hash_info['ruby']['jruby'] = jruby? if jruby?
-
-
1
if libxml2?
-
1
hash_info['libxml'] = {}
-
1
hash_info['libxml']['binding'] = 'extension'
-
1
hash_info['libxml']['compiled'] = compiled_parser_version
-
1
hash_info['libxml']['loaded'] = loaded_parser_version
-
1
hash_info['warnings'] = warnings
-
end
-
-
1
hash_info
-
end
-
-
1
def to_markdown
-
begin
-
require 'psych'
-
rescue LoadError
-
end
-
require 'yaml'
-
"# Nokogiri (#{Nokogiri::VERSION})\n" +
-
YAML.dump(to_hash).each_line.map { |line| " #{line}" }.join
-
end
-
-
# FIXME: maybe switch to singleton?
-
1
@@instance = new
-
1
@@instance.warnings.each do |warning|
-
warn "WARNING: #{warning}"
-
end
-
3
def self.instance; @@instance; end
-
end
-
-
# More complete version information about libxml
-
1
VERSION_INFO = VersionInfo.instance.to_hash
-
-
1
def self.uses_libxml? # :nodoc:
-
VersionInfo.instance.libxml2?
-
end
-
-
1
def self.jruby? # :nodoc:
-
1
VersionInfo.instance.jruby?
-
end
-
end
-
1
require 'nokogiri/xml/pp'
-
1
require 'nokogiri/xml/parse_options'
-
1
require 'nokogiri/xml/sax'
-
1
require 'nokogiri/xml/node'
-
1
require 'nokogiri/xml/attribute_decl'
-
1
require 'nokogiri/xml/element_decl'
-
1
require 'nokogiri/xml/element_content'
-
1
require 'nokogiri/xml/character_data'
-
1
require 'nokogiri/xml/namespace'
-
1
require 'nokogiri/xml/attr'
-
1
require 'nokogiri/xml/dtd'
-
1
require 'nokogiri/xml/cdata'
-
1
require 'nokogiri/xml/text'
-
1
require 'nokogiri/xml/document'
-
1
require 'nokogiri/xml/document_fragment'
-
1
require 'nokogiri/xml/processing_instruction'
-
1
require 'nokogiri/xml/node_set'
-
1
require 'nokogiri/xml/syntax_error'
-
1
require 'nokogiri/xml/xpath'
-
1
require 'nokogiri/xml/xpath_context'
-
1
require 'nokogiri/xml/builder'
-
1
require 'nokogiri/xml/reader'
-
1
require 'nokogiri/xml/notation'
-
1
require 'nokogiri/xml/entity_decl'
-
1
require 'nokogiri/xml/schema'
-
1
require 'nokogiri/xml/relax_ng'
-
-
1
module Nokogiri
-
1
class << self
-
###
-
# Parse XML. Convenience method for Nokogiri::XML::Document.parse
-
1
def XML thing, url = nil, encoding = nil, options = XML::ParseOptions::DEFAULT_XML, &block
-
Nokogiri::XML::Document.parse(thing, url, encoding, options, &block)
-
end
-
end
-
-
1
module XML
-
# Original C14N 1.0 spec canonicalization
-
1
XML_C14N_1_0 = 0
-
# Exclusive C14N 1.0 spec canonicalization
-
1
XML_C14N_EXCLUSIVE_1_0 = 1
-
# C14N 1.1 spec canonicalization
-
1
XML_C14N_1_1 = 2
-
1
class << self
-
###
-
# Parse an XML document using the Nokogiri::XML::Reader API. See
-
# Nokogiri::XML::Reader for mor information
-
1
def Reader string_or_io, url = nil, encoding = nil, options = ParseOptions::STRICT
-
-
options = Nokogiri::XML::ParseOptions.new(options) if Fixnum === options
-
# Give the options to the user
-
yield options if block_given?
-
-
if string_or_io.respond_to? :read
-
return Reader.from_io(string_or_io, url, encoding, options.to_i)
-
end
-
Reader.from_memory(string_or_io, url, encoding, options.to_i)
-
end
-
-
###
-
# Parse XML. Convenience method for Nokogiri::XML::Document.parse
-
1
def parse thing, url = nil, encoding = nil, options = ParseOptions::DEFAULT_XML, &block
-
Document.parse(thing, url, encoding, options, &block)
-
end
-
-
####
-
# Parse a fragment from +string+ in to a NodeSet.
-
1
def fragment string
-
XML::DocumentFragment.parse(string)
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class Attr < Node
-
1
alias :value :content
-
1
alias :to_s :content
-
1
alias :content= :value=
-
-
1
private
-
1
def inspect_attributes
-
[:name, :namespace, :value]
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
###
-
# Represents an attribute declaration in a DTD
-
1
class AttributeDecl < Nokogiri::XML::Node
-
1
undef_method :attribute_nodes
-
1
undef_method :attributes
-
1
undef_method :content
-
1
undef_method :namespace
-
1
undef_method :namespace_definitions
-
1
undef_method :line if method_defined?(:line)
-
-
1
def inspect
-
"#<#{self.class.name}:#{sprintf("0x%x", object_id)} #{to_s.inspect}>"
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
###
-
# Nokogiri builder can be used for building XML and HTML documents.
-
#
-
# == Synopsis:
-
#
-
# builder = Nokogiri::XML::Builder.new do |xml|
-
# xml.root {
-
# xml.products {
-
# xml.widget {
-
# xml.id_ "10"
-
# xml.name "Awesome widget"
-
# }
-
# }
-
# }
-
# end
-
# puts builder.to_xml
-
#
-
# Will output:
-
#
-
# <?xml version="1.0"?>
-
# <root>
-
# <products>
-
# <widget>
-
# <id>10</id>
-
# <name>Awesome widget</name>
-
# </widget>
-
# </products>
-
# </root>
-
#
-
#
-
# === Builder scope
-
#
-
# The builder allows two forms. When the builder is supplied with a block
-
# that has a parameter, the outside scope is maintained. This means you
-
# can access variables that are outside your builder. If you don't need
-
# outside scope, you can use the builder without the "xml" prefix like
-
# this:
-
#
-
# builder = Nokogiri::XML::Builder.new do
-
# root {
-
# products {
-
# widget {
-
# id_ "10"
-
# name "Awesome widget"
-
# }
-
# }
-
# }
-
# end
-
#
-
# == Special Tags
-
#
-
# The builder works by taking advantage of method_missing. Unfortunately
-
# some methods are defined in ruby that are difficult or dangerous to
-
# remove. You may want to create tags with the name "type", "class", and
-
# "id" for example. In that case, you can use an underscore to
-
# disambiguate your tag name from the method call.
-
#
-
# Here is an example of using the underscore to disambiguate tag names from
-
# ruby methods:
-
#
-
# @objects = [Object.new, Object.new, Object.new]
-
#
-
# builder = Nokogiri::XML::Builder.new do |xml|
-
# xml.root {
-
# xml.objects {
-
# @objects.each do |o|
-
# xml.object {
-
# xml.type_ o.type
-
# xml.class_ o.class.name
-
# xml.id_ o.id
-
# }
-
# end
-
# }
-
# }
-
# end
-
# puts builder.to_xml
-
#
-
# The underscore may be used with any tag name, and the last underscore
-
# will just be removed. This code will output the following XML:
-
#
-
# <?xml version="1.0"?>
-
# <root>
-
# <objects>
-
# <object>
-
# <type>Object</type>
-
# <class>Object</class>
-
# <id>48390</id>
-
# </object>
-
# <object>
-
# <type>Object</type>
-
# <class>Object</class>
-
# <id>48380</id>
-
# </object>
-
# <object>
-
# <type>Object</type>
-
# <class>Object</class>
-
# <id>48370</id>
-
# </object>
-
# </objects>
-
# </root>
-
#
-
# == Tag Attributes
-
#
-
# Tag attributes may be supplied as method arguments. Here is our
-
# previous example, but using attributes rather than tags:
-
#
-
# @objects = [Object.new, Object.new, Object.new]
-
#
-
# builder = Nokogiri::XML::Builder.new do |xml|
-
# xml.root {
-
# xml.objects {
-
# @objects.each do |o|
-
# xml.object(:type => o.type, :class => o.class, :id => o.id)
-
# end
-
# }
-
# }
-
# end
-
# puts builder.to_xml
-
#
-
# === Tag Attribute Short Cuts
-
#
-
# A couple attribute short cuts are available when building tags. The
-
# short cuts are available by special method calls when building a tag.
-
#
-
# This example builds an "object" tag with the class attribute "classy"
-
# and the id of "thing":
-
#
-
# builder = Nokogiri::XML::Builder.new do |xml|
-
# xml.root {
-
# xml.objects {
-
# xml.object.classy.thing!
-
# }
-
# }
-
# end
-
# puts builder.to_xml
-
#
-
# Which will output:
-
#
-
# <?xml version="1.0"?>
-
# <root>
-
# <objects>
-
# <object class="classy" id="thing"/>
-
# </objects>
-
# </root>
-
#
-
# All other options are still supported with this syntax, including
-
# blocks and extra tag attributes.
-
#
-
# == Namespaces
-
#
-
# Namespaces are added similarly to attributes. Nokogiri::XML::Builder
-
# assumes that when an attribute starts with "xmlns", it is meant to be
-
# a namespace:
-
#
-
# builder = Nokogiri::XML::Builder.new { |xml|
-
# xml.root('xmlns' => 'default', 'xmlns:foo' => 'bar') do
-
# xml.tenderlove
-
# end
-
# }
-
# puts builder.to_xml
-
#
-
# Will output XML like this:
-
#
-
# <?xml version="1.0"?>
-
# <root xmlns:foo="bar" xmlns="default">
-
# <tenderlove/>
-
# </root>
-
#
-
# === Referencing declared namespaces
-
#
-
# Tags that reference non-default namespaces (i.e. a tag "foo:bar") can be
-
# built by using the Nokogiri::XML::Builder#[] method.
-
#
-
# For example:
-
#
-
# builder = Nokogiri::XML::Builder.new do |xml|
-
# xml.root('xmlns:foo' => 'bar') {
-
# xml.objects {
-
# xml['foo'].object.classy.thing!
-
# }
-
# }
-
# end
-
# puts builder.to_xml
-
#
-
# Will output this XML:
-
#
-
# <?xml version="1.0"?>
-
# <root xmlns:foo="bar">
-
# <objects>
-
# <foo:object class="classy" id="thing"/>
-
# </objects>
-
# </root>
-
#
-
# Note the "foo:object" tag.
-
#
-
# == Document Types
-
#
-
# To create a document type (DTD), access use the Builder#doc method to get
-
# the current context document. Then call Node#create_internal_subset to
-
# create the DTD node.
-
#
-
# For example, this Ruby:
-
#
-
# builder = Nokogiri::XML::Builder.new do |xml|
-
# xml.doc.create_internal_subset(
-
# 'html',
-
# "-//W3C//DTD HTML 4.01 Transitional//EN",
-
# "http://www.w3.org/TR/html4/loose.dtd"
-
# )
-
# xml.root do
-
# xml.foo
-
# end
-
# end
-
#
-
# puts builder.to_xml
-
#
-
# Will output this xml:
-
#
-
# <?xml version="1.0"?>
-
# <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
# <root>
-
# <foo/>
-
# </root>
-
#
-
1
class Builder
-
# The current Document object being built
-
1
attr_accessor :doc
-
-
# The parent of the current node being built
-
1
attr_accessor :parent
-
-
# A context object for use when the block has no arguments
-
1
attr_accessor :context
-
-
1
attr_accessor :arity # :nodoc:
-
-
###
-
# Create a builder with an existing root object. This is for use when
-
# you have an existing document that you would like to augment with
-
# builder methods. The builder context created will start with the
-
# given +root+ node.
-
#
-
# For example:
-
#
-
# doc = Nokogiri::XML(open('somedoc.xml'))
-
# Nokogiri::XML::Builder.with(doc.at('some_tag')) do |xml|
-
# # ... Use normal builder methods here ...
-
# xml.awesome # add the "awesome" tag below "some_tag"
-
# end
-
#
-
1
def self.with root, &block
-
new({}, root, &block)
-
end
-
-
###
-
# Create a new Builder object. +options+ are sent to the top level
-
# Document that is being built.
-
#
-
# Building a document with a particular encoding for example:
-
#
-
# Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
-
# ...
-
# end
-
1
def initialize options = {}, root = nil, &block
-
-
if root
-
@doc = root.document
-
@parent = root
-
else
-
namespace = self.class.name.split('::')
-
namespace[-1] = 'Document'
-
@doc = eval(namespace.join('::')).new
-
@parent = @doc
-
end
-
-
@context = nil
-
@arity = nil
-
@ns = nil
-
-
options.each do |k,v|
-
@doc.send(:"#{k}=", v)
-
end
-
-
return unless block_given?
-
-
@arity = block.arity
-
if @arity <= 0
-
@context = eval('self', block.binding)
-
instance_eval(&block)
-
else
-
yield self
-
end
-
-
@parent = @doc
-
end
-
-
###
-
# Create a Text Node with content of +string+
-
1
def text string
-
insert @doc.create_text_node(string)
-
end
-
-
###
-
# Create a CDATA Node with content of +string+
-
1
def cdata string
-
insert doc.create_cdata(string)
-
end
-
-
###
-
# Create a Comment Node with content of +string+
-
1
def comment string
-
insert doc.create_comment(string)
-
end
-
-
###
-
# Build a tag that is associated with namespace +ns+. Raises an
-
# ArgumentError if +ns+ has not been defined higher in the tree.
-
1
def [] ns
-
@ns = @parent.namespace_definitions.find { |x| x.prefix == ns.to_s }
-
return self if @ns
-
-
@parent.ancestors.each do |a|
-
next if a == doc
-
@ns = a.namespace_definitions.find { |x| x.prefix == ns.to_s }
-
return self if @ns
-
end
-
-
raise ArgumentError, "Namespace #{ns} has not been defined"
-
end
-
-
###
-
# Convert this Builder object to XML
-
1
def to_xml(*args)
-
if Nokogiri.jruby?
-
options = args.first.is_a?(Hash) ? args.shift : {}
-
if !options[:save_with]
-
options[:save_with] = Node::SaveOptions::AS_BUILDER
-
end
-
args.insert(0, options)
-
end
-
@doc.to_xml(*args)
-
end
-
-
###
-
# Append the given raw XML +string+ to the document
-
1
def << string
-
@doc.fragment(string).children.each { |x| insert(x) }
-
end
-
-
1
def method_missing method, *args, &block # :nodoc:
-
if @context && @context.respond_to?(method)
-
@context.send(method, *args, &block)
-
else
-
node = @doc.create_element(method.to_s.sub(/[_!]$/, ''),*args) { |n|
-
# Set up the namespace
-
if @ns
-
n.namespace = @ns
-
@ns = nil
-
end
-
}
-
insert(node, &block)
-
end
-
end
-
-
1
private
-
###
-
# Insert +node+ as a child of the current Node
-
1
def insert(node, &block)
-
node.parent = @parent
-
if block_given?
-
old_parent = @parent
-
@parent = node
-
@arity ||= block.arity
-
if @arity <= 0
-
instance_eval(&block)
-
else
-
block.call(self)
-
end
-
@parent = old_parent
-
end
-
NodeBuilder.new(node, self)
-
end
-
-
1
class NodeBuilder # :nodoc:
-
1
def initialize node, doc_builder
-
@node = node
-
@doc_builder = doc_builder
-
end
-
-
1
def []= k, v
-
@node[k] = v
-
end
-
-
1
def [] k
-
@node[k]
-
end
-
-
1
def method_missing(method, *args, &block)
-
opts = args.last.is_a?(Hash) ? args.pop : {}
-
case method.to_s
-
when /^(.*)!$/
-
@node['id'] = $1
-
@node.content = args.first if args.first
-
when /^(.*)=/
-
@node[$1] = args.first
-
else
-
@node['class'] =
-
((@node['class'] || '').split(/\s/) + [method.to_s]).join(' ')
-
@node.content = args.first if args.first
-
end
-
-
# Assign any extra options
-
opts.each do |k,v|
-
@node[k.to_s] = ((@node[k.to_s] || '').split(/\s/) + [v]).join(' ')
-
end
-
-
if block_given?
-
old_parent = @doc_builder.parent
-
@doc_builder.parent = @node
-
value = @doc_builder.instance_eval(&block)
-
@doc_builder.parent = old_parent
-
return value
-
end
-
self
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class CDATA < Nokogiri::XML::Text
-
###
-
# Get the name of this CDATA node
-
1
def name
-
'#cdata-section'
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class CharacterData < Nokogiri::XML::Node
-
1
include Nokogiri::XML::PP::CharacterData
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
##
-
# Nokogiri::XML::Document is the main entry point for dealing with
-
# XML documents. The Document is created by parsing an XML document.
-
# See Nokogiri.XML()
-
#
-
# For searching a Document, see Nokogiri::XML::Node#css and
-
# Nokogiri::XML::Node#xpath
-
1
class Document < Nokogiri::XML::Node
-
# I'm ignoring unicode characters here.
-
# See http://www.w3.org/TR/REC-xml-names/#ns-decl for more details.
-
1
NCNAME_START_CHAR = "A-Za-z_"
-
1
NCNAME_CHAR = NCNAME_START_CHAR + "\\-.0-9"
-
1
NCNAME_RE = /^xmlns(:[#{NCNAME_START_CHAR}][#{NCNAME_CHAR}]*)?$/
-
-
##
-
# Parse an XML file. +string_or_io+ may be a String, or any object that
-
# responds to _read_ and _close_ such as an IO, or StringIO.
-
# +url+ is resource where this document is located. +encoding+ is the
-
# encoding that should be used when processing the document. +options+
-
# is a number that sets options in the parser, such as
-
# Nokogiri::XML::ParseOptions::RECOVER. See the constants in
-
# Nokogiri::XML::ParseOptions.
-
1
def self.parse string_or_io, url = nil, encoding = nil, options = ParseOptions::DEFAULT_XML, &block
-
options = Nokogiri::XML::ParseOptions.new(options) if Fixnum === options
-
# Give the options to the user
-
yield options if block_given?
-
-
doc = if string_or_io.respond_to?(:read)
-
url ||= string_or_io.respond_to?(:path) ? string_or_io.path : nil
-
read_io(string_or_io, url, encoding, options.to_i)
-
else
-
# read_memory pukes on empty docs
-
return new if string_or_io.nil? or string_or_io.empty?
-
read_memory(string_or_io, url, encoding, options.to_i)
-
end
-
-
# do xinclude processing
-
doc.do_xinclude(options) if options.xinclude?
-
-
return doc
-
end
-
-
# A list of Nokogiri::XML::SyntaxError found when parsing a document
-
1
attr_accessor :errors
-
-
1
def initialize *args # :nodoc:
-
@errors = []
-
@decorators = nil
-
end
-
-
##
-
# Create an element with +name+, and optionally setting the content and attributes.
-
#
-
# doc.create_element "div" # <div></div>
-
# doc.create_element "div", :class => "container" # <div class='container'></div>
-
# doc.create_element "div", "contents" # <div>contents</div>
-
# doc.create_element "div", "contents", :class => "container" # <div class='container'>contents</div>
-
# doc.create_element "div" { |node| node['class'] = "container" } # <div class='container'></div>
-
#
-
1
def create_element name, *args, &block
-
elm = Nokogiri::XML::Element.new(name, self, &block)
-
args.each do |arg|
-
case arg
-
when Hash
-
arg.each { |k,v|
-
key = k.to_s
-
if key =~ NCNAME_RE
-
ns_name = key.split(":", 2)[1]
-
elm.add_namespace_definition ns_name, v
-
next
-
end
-
elm[k.to_s] = v.to_s
-
}
-
else
-
elm.content = arg
-
end
-
end
-
elm
-
end
-
-
# Create a Text Node with +string+
-
1
def create_text_node string, &block
-
Nokogiri::XML::Text.new string.to_s, self, &block
-
end
-
-
# Create a CDATA Node containing +string+
-
1
def create_cdata string, &block
-
Nokogiri::XML::CDATA.new self, string.to_s, &block
-
end
-
-
# Create a Comment Node containing +string+
-
1
def create_comment string, &block
-
Nokogiri::XML::Comment.new self, string.to_s, &block
-
end
-
-
# The name of this document. Always returns "document"
-
1
def name
-
'document'
-
end
-
-
# A reference to +self+
-
1
def document
-
self
-
end
-
-
##
-
# Recursively get all namespaces from this node and its subtree and
-
# return them as a hash.
-
#
-
# For example, given this document:
-
#
-
# <root xmlns:foo="bar">
-
# <bar xmlns:hello="world" />
-
# </root>
-
#
-
# This method will return:
-
#
-
# { 'xmlns:foo' => 'bar', 'xmlns:hello' => 'world' }
-
#
-
# WARNING: this method will clobber duplicate names in the keys.
-
# For example, given this document:
-
#
-
# <root xmlns:foo="bar">
-
# <bar xmlns:foo="baz" />
-
# </root>
-
#
-
# The hash returned will look like this: { 'xmlns:foo' => 'bar' }
-
#
-
# Non-prefixed default namespaces (as in "xmlns=") are not included
-
# in the hash.
-
#
-
# Note this is a very expensive operation in current implementation, as it
-
# traverses the entire graph, and also has to bring each node accross the
-
# libxml bridge into a ruby object.
-
1
def collect_namespaces
-
ns = {}
-
traverse { |j| ns.merge!(j.namespaces) }
-
ns
-
end
-
-
# Get the list of decorators given +key+
-
1
def decorators key
-
@decorators ||= Hash.new
-
@decorators[key] ||= []
-
end
-
-
##
-
# Validate this Document against it's DTD. Returns a list of errors on
-
# the document or +nil+ when there is no DTD.
-
1
def validate
-
return nil unless internal_subset
-
internal_subset.validate self
-
end
-
-
##
-
# Explore a document with shortcut methods. See Nokogiri::Slop for details.
-
#
-
# Note that any nodes that have been instantiated before #slop!
-
# is called will not be decorated with sloppy behavior. So, if you're in
-
# irb, the preferred idiom is:
-
#
-
# irb> doc = Nokogiri::Slop my_markup
-
#
-
# and not
-
#
-
# irb> doc = Nokogiri::HTML my_markup
-
# ... followed by irb's implicit inspect (and therefore instantiation of every node) ...
-
# irb> doc.slop!
-
# ... which does absolutely nothing.
-
#
-
1
def slop!
-
unless decorators(XML::Node).include? Nokogiri::Decorators::Slop
-
decorators(XML::Node) << Nokogiri::Decorators::Slop
-
decorate!
-
end
-
-
self
-
end
-
-
##
-
# Apply any decorators to +node+
-
1
def decorate node
-
return unless @decorators
-
@decorators.each { |klass,list|
-
next unless node.is_a?(klass)
-
list.each { |moodule| node.extend(moodule) }
-
}
-
end
-
-
1
alias :to_xml :serialize
-
1
alias :clone :dup
-
-
# Get the hash of namespaces on the root Nokogiri::XML::Node
-
1
def namespaces
-
root ? root.namespaces : {}
-
end
-
-
##
-
# Create a Nokogiri::XML::DocumentFragment from +tags+
-
# Returns an empty fragment if +tags+ is nil.
-
1
def fragment tags = nil
-
DocumentFragment.new(self, tags, self.root)
-
end
-
-
1
undef_method :swap, :parent, :namespace, :default_namespace=
-
1
undef_method :add_namespace_definition, :attributes
-
1
undef_method :namespace_definitions, :line, :add_namespace
-
-
1
def add_child node_or_tags
-
raise "Document already has a root node" if root
-
node_or_tags = coerce(node_or_tags)
-
if node_or_tags.is_a?(XML::NodeSet)
-
raise "Document cannot have multiple root nodes" if node_or_tags.size > 1
-
super(node_or_tags.first)
-
else
-
super
-
end
-
end
-
1
alias :<< :add_child
-
-
##
-
# +JRuby+
-
# Wraps Java's org.w3c.dom.document and returns Nokogiri::XML::Document
-
1
def self.wrap document
-
raise "JRuby only method" unless Nokogiri.jruby?
-
return wrapJavaDocument(document)
-
end
-
-
##
-
# +JRuby+
-
# Returns Java's org.w3c.dom.document of this Document.
-
1
def to_java
-
raise "JRuby only method" unless Nokogiri.jruby?
-
return toJavaDocument()
-
end
-
-
1
private
-
1
def implied_xpath_context
-
"/"
-
end
-
-
1
def inspect_attributes
-
[:name, :children]
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class DocumentFragment < Nokogiri::XML::Node
-
##
-
# Create a new DocumentFragment from +tags+.
-
#
-
# If +ctx+ is present, it is used as a context node for the
-
# subtree created, e.g., namespaces will be resolved relative
-
# to +ctx+.
-
1
def initialize document, tags = nil, ctx = nil
-
return self unless tags
-
-
children = if ctx
-
# Fix for issue#490
-
if Nokogiri.jruby?
-
ctx.parse("<root>#{tags}</root>").xpath("/root/node()")
-
else
-
ctx.parse(tags)
-
end
-
else
-
XML::Document.parse("<root>#{tags}</root>") \
-
.xpath("/root/node()")
-
end
-
children.each { |child| child.parent = self }
-
end
-
-
###
-
# return the name for DocumentFragment
-
1
def name
-
'#document-fragment'
-
end
-
-
###
-
# Convert this DocumentFragment to a string
-
1
def to_s
-
children.to_s
-
end
-
-
###
-
# Convert this DocumentFragment to html
-
# See Nokogiri::XML::NodeSet#to_html
-
1
def to_html *args
-
if Nokogiri.jruby?
-
options = args.first.is_a?(Hash) ? args.shift : {}
-
if !options[:save_with]
-
options[:save_with] = Node::SaveOptions::NO_DECLARATION | Node::SaveOptions::NO_EMPTY_TAGS | Node::SaveOptions::AS_HTML
-
end
-
args.insert(0, options)
-
end
-
children.to_html(*args)
-
end
-
-
###
-
# Convert this DocumentFragment to xhtml
-
# See Nokogiri::XML::NodeSet#to_xhtml
-
1
def to_xhtml *args
-
if Nokogiri.jruby?
-
options = args.first.is_a?(Hash) ? args.shift : {}
-
if !options[:save_with]
-
options[:save_with] = Node::SaveOptions::NO_DECLARATION | Node::SaveOptions::NO_EMPTY_TAGS | Node::SaveOptions::AS_XHTML
-
end
-
args.insert(0, options)
-
end
-
children.to_xhtml(*args)
-
end
-
-
###
-
# Convert this DocumentFragment to xml
-
# See Nokogiri::XML::NodeSet#to_xml
-
1
def to_xml *args
-
children.to_xml(*args)
-
end
-
-
###
-
# Search this fragment. See Nokogiri::XML::Node#css
-
1
def css *args
-
if children.any?
-
children.css(*args)
-
else
-
NodeSet.new(document)
-
end
-
end
-
-
1
alias :serialize :to_s
-
-
1
class << self
-
####
-
# Create a Nokogiri::XML::DocumentFragment from +tags+
-
1
def parse tags
-
self.new(XML::Document.new, tags)
-
end
-
end
-
-
1
private
-
-
1
def coerce data
-
return super unless String === data
-
-
document.fragment(data).children
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class DTD < Nokogiri::XML::Node
-
1
undef_method :attribute_nodes
-
1
undef_method :values
-
1
undef_method :content
-
1
undef_method :namespace
-
1
undef_method :namespace_definitions
-
1
undef_method :line if method_defined?(:line)
-
-
1
def keys
-
attributes.keys
-
end
-
-
1
def each &block
-
attributes.each { |key, value|
-
block.call([key, value])
-
}
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
###
-
# Represents the allowed content in an Element Declaration inside a DTD:
-
#
-
# <?xml version="1.0"?><?TEST-STYLE PIDATA?>
-
# <!DOCTYPE staff SYSTEM "staff.dtd" [
-
# <!ELEMENT div1 (head, (p | list | note)*, div2*)>
-
# ]>
-
# </root>
-
#
-
# ElementContent represents the tree inside the <!ELEMENT> tag shown above
-
# that lists the possible content for the div1 tag.
-
1
class ElementContent
-
# Possible definitions of type
-
1
PCDATA = 1
-
1
ELEMENT = 2
-
1
SEQ = 3
-
1
OR = 4
-
-
# Possible content occurrences
-
1
ONCE = 1
-
1
OPT = 2
-
1
MULT = 3
-
1
PLUS = 4
-
-
1
attr_reader :document
-
-
###
-
# Get the children of this ElementContent node
-
1
def children
-
[c1, c2].compact
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class ElementDecl < Nokogiri::XML::Node
-
1
undef_method :namespace
-
1
undef_method :namespace_definitions
-
1
undef_method :line if method_defined?(:line)
-
-
1
def inspect
-
"#<#{self.class.name}:#{sprintf("0x%x", object_id)} #{to_s.inspect}>"
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class EntityDecl < Nokogiri::XML::Node
-
1
undef_method :attribute_nodes
-
1
undef_method :attributes
-
1
undef_method :namespace
-
1
undef_method :namespace_definitions
-
1
undef_method :line if method_defined?(:line)
-
-
1
def self.new name, doc, *args
-
doc.create_entity(name, *args)
-
end
-
-
1
def inspect
-
"#<#{self.class.name}:#{sprintf("0x%x", object_id)} #{to_s.inspect}>"
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class Namespace
-
1
include Nokogiri::XML::PP::Node
-
1
attr_reader :document
-
-
1
private
-
1
def inspect_attributes
-
[:prefix, :href]
-
end
-
end
-
end
-
end
-
1
require 'stringio'
-
1
require 'nokogiri/xml/node/save_options'
-
-
1
module Nokogiri
-
1
module XML
-
####
-
# Nokogiri::XML::Node is your window to the fun filled world of dealing
-
# with XML and HTML tags. A Nokogiri::XML::Node may be treated similarly
-
# to a hash with regard to attributes. For example (from irb):
-
#
-
# irb(main):004:0> node
-
# => <a href="#foo" id="link">link</a>
-
# irb(main):005:0> node['href']
-
# => "#foo"
-
# irb(main):006:0> node.keys
-
# => ["href", "id"]
-
# irb(main):007:0> node.values
-
# => ["#foo", "link"]
-
# irb(main):008:0> node['class'] = 'green'
-
# => "green"
-
# irb(main):009:0> node
-
# => <a href="#foo" id="link" class="green">link</a>
-
# irb(main):010:0>
-
#
-
# See Nokogiri::XML::Node#[] and Nokogiri::XML#[]= for more information.
-
#
-
# Nokogiri::XML::Node also has methods that let you move around your
-
# tree. For navigating your tree, see:
-
#
-
# * Nokogiri::XML::Node#parent
-
# * Nokogiri::XML::Node#children
-
# * Nokogiri::XML::Node#next
-
# * Nokogiri::XML::Node#previous
-
#
-
# You may search this node's subtree using Node#xpath and Node#css
-
1
class Node
-
1
include Nokogiri::XML::PP::Node
-
1
include Enumerable
-
-
# Element node type, see Nokogiri::XML::Node#element?
-
1
ELEMENT_NODE = 1
-
# Attribute node type
-
1
ATTRIBUTE_NODE = 2
-
# Text node type, see Nokogiri::XML::Node#text?
-
1
TEXT_NODE = 3
-
# CDATA node type, see Nokogiri::XML::Node#cdata?
-
1
CDATA_SECTION_NODE = 4
-
# Entity reference node type
-
1
ENTITY_REF_NODE = 5
-
# Entity node type
-
1
ENTITY_NODE = 6
-
# PI node type
-
1
PI_NODE = 7
-
# Comment node type, see Nokogiri::XML::Node#comment?
-
1
COMMENT_NODE = 8
-
# Document node type, see Nokogiri::XML::Node#xml?
-
1
DOCUMENT_NODE = 9
-
# Document type node type
-
1
DOCUMENT_TYPE_NODE = 10
-
# Document fragment node type
-
1
DOCUMENT_FRAG_NODE = 11
-
# Notation node type
-
1
NOTATION_NODE = 12
-
# HTML document node type, see Nokogiri::XML::Node#html?
-
1
HTML_DOCUMENT_NODE = 13
-
# DTD node type
-
1
DTD_NODE = 14
-
# Element declaration type
-
1
ELEMENT_DECL = 15
-
# Attribute declaration type
-
1
ATTRIBUTE_DECL = 16
-
# Entity declaration type
-
1
ENTITY_DECL = 17
-
# Namespace declaration type
-
1
NAMESPACE_DECL = 18
-
# XInclude start type
-
1
XINCLUDE_START = 19
-
# XInclude end type
-
1
XINCLUDE_END = 20
-
# DOCB document node type
-
1
DOCB_DOCUMENT_NODE = 21
-
-
1
def initialize name, document # :nodoc:
-
# ... Ya. This is empty on purpose.
-
end
-
-
###
-
# Decorate this node with the decorators set up in this node's Document
-
1
def decorate!
-
document.decorate(self)
-
end
-
-
###
-
# Search this node for +paths+. +paths+ can be XPath or CSS, and an
-
# optional hash of namespaces may be appended.
-
# See Node#xpath and Node#css.
-
1
def search *paths
-
# TODO use paths, handler, ns, binds = extract_params(paths)
-
ns = paths.last.is_a?(Hash) ? paths.pop :
-
(document.root ? document.root.namespaces : {})
-
-
prefix = "#{implied_xpath_context}/"
-
-
xpath(*(paths.map { |path|
-
path = path.to_s
-
path =~ /^(\.\/|\/|\.\.)/ ? path : CSS.xpath_for(
-
path,
-
:prefix => prefix,
-
:ns => ns
-
)
-
}.flatten.uniq) + [ns])
-
end
-
1
alias :/ :search
-
-
###
-
# call-seq: xpath *paths, [namespace-bindings, variable-bindings, custom-handler-class]
-
#
-
# Search this node for XPath +paths+. +paths+ must be one or more XPath
-
# queries.
-
#
-
# node.xpath('.//title')
-
#
-
# A hash of namespace bindings may be appended. For example:
-
#
-
# node.xpath('.//foo:name', {'foo' => 'http://example.org/'})
-
# node.xpath('.//xmlns:name', node.root.namespaces)
-
#
-
# A hash of variable bindings may also be appended to the namespace bindings. For example:
-
#
-
# node.xpath('.//address[@domestic=$value]', nil, {:value => 'Yes'})
-
#
-
# Custom XPath functions may also be defined. To define custom
-
# functions create a class and implement the function you want
-
# to define. The first argument to the method will be the
-
# current matching NodeSet. Any other arguments are ones that
-
# you pass in. Note that this class may appear anywhere in the
-
# argument list. For example:
-
#
-
# node.xpath('.//title[regex(., "\w+")]', Class.new {
-
# def regex node_set, regex
-
# node_set.find_all { |node| node['some_attribute'] =~ /#{regex}/ }
-
# end
-
# }.new)
-
#
-
1
def xpath *paths
-
return NodeSet.new(document) unless document
-
-
paths, handler, ns, binds = extract_params(paths)
-
-
sets = paths.map { |path|
-
ctx = XPathContext.new(self)
-
ctx.register_namespaces(ns)
-
path = path.gsub(/\/xmlns:/,'/:') unless Nokogiri.uses_libxml?
-
-
binds.each do |key,value|
-
ctx.register_variable key.to_s, value
-
end if binds
-
-
ctx.evaluate(path, handler)
-
}
-
return sets.first if sets.length == 1
-
-
NodeSet.new(document) do |combined|
-
sets.each do |set|
-
set.each do |node|
-
combined << node
-
end
-
end
-
end
-
end
-
-
###
-
# call-seq: css *rules, [namespace-bindings, custom-pseudo-class]
-
#
-
# Search this node for CSS +rules+. +rules+ must be one or more CSS
-
# selectors. For example:
-
#
-
# node.css('title')
-
# node.css('body h1.bold')
-
# node.css('div + p.green', 'div#one')
-
#
-
# A hash of namespace bindings may be appended. For example:
-
#
-
# node.css('bike|tire', {'bike' => 'http://schwinn.com/'})
-
#
-
# Custom CSS pseudo classes may also be defined. To define
-
# custom pseudo classes, create a class and implement the custom
-
# pseudo class you want defined. The first argument to the
-
# method will be the current matching NodeSet. Any other
-
# arguments are ones that you pass in. For example:
-
#
-
# node.css('title:regex("\w+")', Class.new {
-
# def regex node_set, regex
-
# node_set.find_all { |node| node['some_attribute'] =~ /#{regex}/ }
-
# end
-
# }.new)
-
#
-
# Note that the CSS query string is case-sensitive with regards
-
# to your document type. That is, if you're looking for "H1" in
-
# an HTML document, you'll never find anything, since HTML tags
-
# will match only lowercase CSS queries. However, "H1" might be
-
# found in an XML document, where tags names are case-sensitive
-
# (e.g., "H1" is distinct from "h1").
-
#
-
1
def css *rules
-
rules, handler, ns, binds = extract_params(rules)
-
-
prefix = "#{implied_xpath_context}/"
-
-
rules = rules.map { |rule|
-
CSS.xpath_for(rule, :prefix => prefix, :ns => ns)
-
}.flatten.uniq + [ns, handler, binds].compact
-
-
xpath(*rules)
-
end
-
-
###
-
# Search this node's immediate children using CSS selector +selector+
-
1
def > selector
-
ns = document.root.namespaces
-
xpath CSS.xpath_for(selector, :prefix => "./", :ns => ns).first
-
end
-
-
###
-
# Search for the first occurrence of +path+.
-
#
-
# Returns nil if nothing is found, otherwise a Node.
-
1
def at path, ns = document.root ? document.root.namespaces : {}
-
search(path, ns).first
-
end
-
1
alias :% :at
-
-
##
-
# Search this node for the first occurrence of XPath +paths+.
-
# Equivalent to <tt>xpath(paths).first</tt>
-
# See Node#xpath for more information.
-
#
-
1
def at_xpath *paths
-
xpath(*paths).first
-
end
-
-
##
-
# Search this node for the first occurrence of CSS +rules+.
-
# Equivalent to <tt>css(rules).first</tt>
-
# See Node#css for more information.
-
#
-
1
def at_css *rules
-
css(*rules).first
-
end
-
-
###
-
# Get the attribute value for the attribute +name+
-
1
def [] name
-
return nil unless key?(name.to_s)
-
get(name.to_s)
-
end
-
-
###
-
# Set the attribute value for the attribute +name+ to +value+
-
1
def []= name, value
-
set name.to_s, value
-
end
-
-
###
-
# Add +node_or_tags+ as a child of this Node.
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
-
#
-
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
-
#
-
# Also see related method +<<+.
-
1
def add_child node_or_tags
-
node_or_tags = coerce(node_or_tags)
-
if node_or_tags.is_a?(XML::NodeSet)
-
node_or_tags.each { |n| add_child_node n }
-
else
-
add_child_node node_or_tags
-
end
-
node_or_tags
-
end
-
-
###
-
# Add +node_or_tags+ as a child of this Node.
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
-
#
-
# Returns self, to support chaining of calls (e.g., root << child1 << child2)
-
#
-
# Also see related method +add_child+.
-
1
def << node_or_tags
-
add_child node_or_tags
-
self
-
end
-
###
-
# Insert +node_or_tags+ before this Node (as a sibling).
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
-
#
-
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
-
#
-
# Also see related method +before+.
-
1
def add_previous_sibling node_or_tags
-
raise ArgumentError.new("A document may not have multiple root nodes.") if parent.is_a?(XML::Document) && !node_or_tags.is_a?(XML::ProcessingInstruction)
-
-
node_or_tags = coerce(node_or_tags)
-
if node_or_tags.is_a?(XML::NodeSet)
-
if text?
-
pivot = Nokogiri::XML::Node.new 'dummy', document
-
add_previous_sibling_node pivot
-
else
-
pivot = self
-
end
-
node_or_tags.each { |n| pivot.send :add_previous_sibling_node, n }
-
pivot.unlink if text?
-
else
-
add_previous_sibling_node node_or_tags
-
end
-
node_or_tags
-
end
-
-
###
-
# Insert +node_or_tags+ after this Node (as a sibling).
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
-
#
-
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
-
#
-
# Also see related method +after+.
-
1
def add_next_sibling node_or_tags
-
raise ArgumentError.new("A document may not have multiple root nodes.") if parent.is_a?(XML::Document)
-
-
node_or_tags = coerce(node_or_tags)
-
if node_or_tags.is_a?(XML::NodeSet)
-
if text?
-
pivot = Nokogiri::XML::Node.new 'dummy', document
-
add_next_sibling_node pivot
-
else
-
pivot = self
-
end
-
node_or_tags.reverse_each { |n| pivot.send :add_next_sibling_node, n }
-
pivot.unlink if text?
-
else
-
add_next_sibling_node node_or_tags
-
end
-
node_or_tags
-
end
-
-
####
-
# Insert +node_or_tags+ before this node (as a sibling).
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
-
#
-
# Returns self, to support chaining of calls.
-
#
-
# Also see related method +add_previous_sibling+.
-
1
def before node_or_tags
-
add_previous_sibling node_or_tags
-
self
-
end
-
-
####
-
# Insert +node_or_tags+ after this node (as a sibling).
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a Nokogiri::XML::DocumentFragment, or a string containing markup.
-
#
-
# Returns self, to support chaining of calls.
-
#
-
# Also see related method +add_next_sibling+.
-
1
def after node_or_tags
-
add_next_sibling node_or_tags
-
self
-
end
-
-
####
-
# Set the inner html for this Node to +node_or_tags+
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a Nokogiri::XML::DocumentFragment, or a string containing markup.
-
#
-
# Returns self.
-
#
-
# Also see related method +children=+
-
1
def inner_html= node_or_tags
-
self.children = node_or_tags
-
self
-
end
-
-
####
-
# Set the inner html for this Node +node_or_tags+
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a Nokogiri::XML::DocumentFragment, or a string containing markup.
-
#
-
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
-
#
-
# Also see related method +inner_html=+
-
1
def children= node_or_tags
-
node_or_tags = coerce(node_or_tags)
-
children.unlink
-
if node_or_tags.is_a?(XML::NodeSet)
-
node_or_tags.each { |n| add_child_node n }
-
else
-
add_child_node node_or_tags
-
end
-
node_or_tags
-
end
-
-
####
-
# Replace this Node with +node_or_tags+.
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
-
#
-
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
-
#
-
# Also see related method +swap+.
-
1
def replace node_or_tags
-
node_or_tags = coerce(node_or_tags)
-
if node_or_tags.is_a?(XML::NodeSet)
-
if text?
-
replacee = Nokogiri::XML::Node.new 'dummy', document
-
add_previous_sibling_node replacee
-
unlink
-
else
-
replacee = self
-
end
-
node_or_tags.each { |n| replacee.add_previous_sibling n }
-
replacee.unlink
-
else
-
replace_node node_or_tags
-
end
-
node_or_tags
-
end
-
-
####
-
# Swap this Node for +node_or_tags+
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
-
#
-
# Returns self, to support chaining of calls.
-
#
-
# Also see related method +replace+.
-
1
def swap node_or_tags
-
replace node_or_tags
-
self
-
end
-
-
1
alias :next :next_sibling
-
1
alias :previous :previous_sibling
-
-
# :stopdoc:
-
# HACK: This is to work around an RDoc bug
-
1
alias :next= :add_next_sibling
-
# :startdoc:
-
-
1
alias :previous= :add_previous_sibling
-
1
alias :remove :unlink
-
1
alias :get_attribute :[]
-
1
alias :attr :[]
-
1
alias :set_attribute :[]=
-
1
alias :text :content
-
1
alias :inner_text :content
-
1
alias :has_attribute? :key?
-
1
alias :name :node_name
-
1
alias :name= :node_name=
-
1
alias :type :node_type
-
1
alias :to_str :text
-
1
alias :clone :dup
-
1
alias :elements :element_children
-
-
####
-
# Returns a hash containing the node's attributes. The key is
-
# the attribute name without any namespace, the value is a Nokogiri::XML::Attr
-
# representing the attribute.
-
# If you need to distinguish attributes with the same name, with different namespaces
-
# use #attribute_nodes instead.
-
1
def attributes
-
Hash[attribute_nodes.map { |node|
-
[node.node_name, node]
-
}]
-
end
-
-
###
-
# Get the attribute values for this Node.
-
1
def values
-
attribute_nodes.map { |node| node.value }
-
end
-
-
###
-
# Get the attribute names for this Node.
-
1
def keys
-
attribute_nodes.map { |node| node.node_name }
-
end
-
-
###
-
# Iterate over each attribute name and value pair for this Node.
-
1
def each
-
attribute_nodes.each { |node|
-
yield [node.node_name, node.value]
-
}
-
end
-
-
###
-
# Remove the attribute named +name+
-
1
def remove_attribute name
-
attributes[name].remove if key? name
-
end
-
1
alias :delete :remove_attribute
-
-
###
-
# Returns true if this Node matches +selector+
-
1
def matches? selector
-
ancestors.last.search(selector).include?(self)
-
end
-
-
###
-
# Create a DocumentFragment containing +tags+ that is relative to _this_
-
# context node.
-
1
def fragment tags
-
type = document.html? ? Nokogiri::HTML : Nokogiri::XML
-
type::DocumentFragment.new(document, tags, self)
-
end
-
-
###
-
# Parse +string_or_io+ as a document fragment within the context of
-
# *this* node. Returns a XML::NodeSet containing the nodes parsed from
-
# +string_or_io+.
-
1
def parse string_or_io, options = nil
-
options ||= (document.html? ? ParseOptions::DEFAULT_HTML : ParseOptions::DEFAULT_XML)
-
if Fixnum === options
-
options = Nokogiri::XML::ParseOptions.new(options)
-
end
-
# Give the options to the user
-
yield options if block_given?
-
-
contents = string_or_io.respond_to?(:read) ?
-
string_or_io.read :
-
string_or_io
-
-
return Nokogiri::XML::NodeSet.new(document) if contents.empty?
-
-
##
-
# This is a horrible hack, but I don't care. See #313 for background.
-
error_count = document.errors.length
-
node_set = in_context(contents, options.to_i)
-
if node_set.empty? and document.errors.length > error_count and options.recover?
-
fragment = Nokogiri::HTML::DocumentFragment.parse contents
-
node_set = fragment.children
-
end
-
node_set
-
end
-
-
####
-
# Set the Node's content to a Text node containing +string+. The string gets XML escaped, not interpreted as markup.
-
1
def content= string
-
self.native_content = encode_special_chars(string.to_s)
-
end
-
-
###
-
# Set the parent Node for this Node
-
1
def parent= parent_node
-
parent_node.add_child(self)
-
parent_node
-
end
-
-
###
-
# Returns a Hash of {prefix => value} for all namespaces on this
-
# node and its ancestors.
-
#
-
# This method returns the same namespaces as #namespace_scopes.
-
#
-
# Returns namespaces in scope for self -- those defined on self
-
# element directly or any ancestor node -- as a Hash of
-
# attribute-name/value pairs. Note that the keys in this hash
-
# XML attributes that would be used to define this namespace,
-
# such as "xmlns:prefix", not just the prefix. Default namespace
-
# set on self will be included with key "xmlns". However,
-
# default namespaces set on ancestor will NOT be, even if self
-
# has no explicit default namespace.
-
1
def namespaces
-
Hash[namespace_scopes.map { |nd|
-
key = ['xmlns', nd.prefix].compact.join(':')
-
if RUBY_VERSION >= '1.9' && document.encoding
-
begin
-
key.force_encoding document.encoding
-
rescue ArgumentError
-
end
-
end
-
[key, nd.href]
-
}]
-
end
-
-
# Returns true if this is a Comment
-
1
def comment?
-
type == COMMENT_NODE
-
end
-
-
# Returns true if this is a CDATA
-
1
def cdata?
-
type == CDATA_SECTION_NODE
-
end
-
-
# Returns true if this is an XML::Document node
-
1
def xml?
-
type == DOCUMENT_NODE
-
end
-
-
# Returns true if this is an HTML::Document node
-
1
def html?
-
type == HTML_DOCUMENT_NODE
-
end
-
-
# Returns true if this is a Text node
-
1
def text?
-
type == TEXT_NODE
-
end
-
-
# Returns true if this is a DocumentFragment
-
1
def fragment?
-
type == DOCUMENT_FRAG_NODE
-
end
-
-
###
-
# Fetch the Nokogiri::HTML::ElementDescription for this node. Returns
-
# nil on XML documents and on unknown tags.
-
1
def description
-
return nil if document.xml?
-
Nokogiri::HTML::ElementDescription[name]
-
end
-
-
###
-
# Is this a read only node?
-
1
def read_only?
-
# According to gdome2, these are read-only node types
-
[NOTATION_NODE, ENTITY_NODE, ENTITY_DECL].include?(type)
-
end
-
-
# Returns true if this is an Element node
-
1
def element?
-
type == ELEMENT_NODE
-
end
-
1
alias :elem? :element?
-
-
###
-
# Turn this node in to a string. If the document is HTML, this method
-
# returns html. If the document is XML, this method returns XML.
-
1
def to_s
-
document.xml? ? to_xml : to_html
-
end
-
-
# Get the inner_html for this node's Node#children
-
1
def inner_html *args
-
children.map { |x| x.to_html(*args) }.join
-
end
-
-
# Get the path to this node as a CSS expression
-
1
def css_path
-
path.split(/\//).map { |part|
-
part.length == 0 ? nil : part.gsub(/\[(\d+)\]/, ':nth-of-type(\1)')
-
}.compact.join(' > ')
-
end
-
-
###
-
# Get a list of ancestor Node for this Node. If +selector+ is given,
-
# the ancestors must match +selector+
-
1
def ancestors selector = nil
-
return NodeSet.new(document) unless respond_to?(:parent)
-
return NodeSet.new(document) unless parent
-
-
parents = [parent]
-
-
while parents.last.respond_to?(:parent)
-
break unless ctx_parent = parents.last.parent
-
parents << ctx_parent
-
end
-
-
return NodeSet.new(document, parents) unless selector
-
-
root = parents.last
-
-
NodeSet.new(document, parents.find_all { |parent|
-
root.search(selector).include?(parent)
-
})
-
end
-
-
###
-
# Adds a default namespace supplied as a string +url+ href, to self.
-
# The consequence is as an xmlns attribute with supplied argument were
-
# present in parsed XML. A default namespace set with this method will
-
# now show up in #attributes, but when this node is serialized to XML an
-
# "xmlns" attribute will appear. See also #namespace and #namespace=
-
1
def default_namespace= url
-
add_namespace_definition(nil, url)
-
end
-
1
alias :add_namespace :add_namespace_definition
-
-
###
-
# Set the default namespace on this node (as would be defined with an
-
# "xmlns=" attribute in XML source), as a Namespace object +ns+. Note that
-
# a Namespace added this way will NOT be serialized as an xmlns attribute
-
# for this node. You probably want #default_namespace= instead, or perhaps
-
# #add_namespace_definition with a nil prefix argument.
-
1
def namespace= ns
-
return set_namespace(ns) unless ns
-
-
unless Nokogiri::XML::Namespace === ns
-
raise TypeError, "#{ns.class} can't be coerced into Nokogiri::XML::Namespace"
-
end
-
if ns.document != document
-
raise ArgumentError, 'namespace must be declared on the same document'
-
end
-
-
set_namespace ns
-
end
-
-
####
-
# Yields self and all children to +block+ recursively.
-
1
def traverse &block
-
children.each{|j| j.traverse(&block) }
-
block.call(self)
-
end
-
-
###
-
# Accept a visitor. This method calls "visit" on +visitor+ with self.
-
1
def accept visitor
-
visitor.visit(self)
-
end
-
-
###
-
# Test to see if this Node is equal to +other+
-
1
def == other
-
return false unless other
-
return false unless other.respond_to?(:pointer_id)
-
pointer_id == other.pointer_id
-
end
-
-
###
-
# Serialize Node using +options+. Save options can also be set using a
-
# block. See SaveOptions.
-
#
-
# These two statements are equivalent:
-
#
-
# node.serialize(:encoding => 'UTF-8', :save_with => FORMAT | AS_XML)
-
#
-
# or
-
#
-
# node.serialize(:encoding => 'UTF-8') do |config|
-
# config.format.as_xml
-
# end
-
#
-
1
def serialize *args, &block
-
options = args.first.is_a?(Hash) ? args.shift : {
-
:encoding => args[0],
-
:save_with => args[1]
-
}
-
-
encoding = options[:encoding] || document.encoding
-
options[:encoding] = encoding
-
-
outstring = ""
-
if encoding && outstring.respond_to?(:force_encoding)
-
outstring.force_encoding(Encoding.find(encoding))
-
end
-
io = StringIO.new(outstring)
-
write_to io, options, &block
-
io.string
-
end
-
-
###
-
# Serialize this Node to HTML
-
#
-
# doc.to_html
-
#
-
# See Node#write_to for a list of +options+. For formatted output,
-
# use Node#to_xhtml instead.
-
1
def to_html options = {}
-
# FIXME: this is a hack around broken libxml versions
-
return dump_html if Nokogiri.uses_libxml? && %w[2 6] === LIBXML_VERSION.split('.')[0..1]
-
-
options[:save_with] |= SaveOptions::DEFAULT_HTML if options[:save_with]
-
options[:save_with] = SaveOptions::DEFAULT_HTML unless options[:save_with]
-
serialize(options)
-
end
-
-
###
-
# Serialize this Node to XML using +options+
-
#
-
# doc.to_xml(:indent => 5, :encoding => 'UTF-8')
-
#
-
# See Node#write_to for a list of +options+
-
1
def to_xml options = {}
-
options[:save_with] ||= SaveOptions::DEFAULT_XML
-
serialize(options)
-
end
-
-
###
-
# Serialize this Node to XHTML using +options+
-
#
-
# doc.to_xhtml(:indent => 5, :encoding => 'UTF-8')
-
#
-
# See Node#write_to for a list of +options+
-
1
def to_xhtml options = {}
-
# FIXME: this is a hack around broken libxml versions
-
return dump_html if Nokogiri.uses_libxml? && %w[2 6] === LIBXML_VERSION.split('.')[0..1]
-
-
options[:save_with] |= SaveOptions::DEFAULT_XHTML if options[:save_with]
-
options[:save_with] = SaveOptions::DEFAULT_XHTML unless options[:save_with]
-
serialize(options)
-
end
-
-
###
-
# Write Node to +io+ with +options+. +options+ modify the output of
-
# this method. Valid options are:
-
#
-
# * +:encoding+ for changing the encoding
-
# * +:indent_text+ the indentation text, defaults to one space
-
# * +:indent+ the number of +:indent_text+ to use, defaults to 2
-
# * +:save_with+ a combination of SaveOptions constants.
-
#
-
# To save with UTF-8 indented twice:
-
#
-
# node.write_to(io, :encoding => 'UTF-8', :indent => 2)
-
#
-
# To save indented with two dashes:
-
#
-
# node.write_to(io, :indent_text => '-', :indent => 2
-
#
-
1
def write_to io, *options
-
options = options.first.is_a?(Hash) ? options.shift : {}
-
encoding = options[:encoding] || options[0]
-
if Nokogiri.jruby?
-
save_options = options[:save_with] || options[1]
-
indent_times = options[:indent] || 0
-
else
-
save_options = options[:save_with] || options[1] || SaveOptions::FORMAT
-
indent_times = options[:indent] || 2
-
end
-
indent_text = options[:indent_text] || ' '
-
-
config = SaveOptions.new(save_options.to_i)
-
yield config if block_given?
-
-
native_write_to(io, encoding, indent_text * indent_times, config.options)
-
end
-
-
###
-
# Write Node as HTML to +io+ with +options+
-
#
-
# See Node#write_to for a list of +options+
-
1
def write_html_to io, options = {}
-
# FIXME: this is a hack around broken libxml versions
-
return (io << dump_html) if Nokogiri.uses_libxml? && %w[2 6] === LIBXML_VERSION.split('.')[0..1]
-
-
options[:save_with] ||= SaveOptions::DEFAULT_HTML
-
write_to io, options
-
end
-
-
###
-
# Write Node as XHTML to +io+ with +options+
-
#
-
# See Node#write_to for a list of +options+
-
1
def write_xhtml_to io, options = {}
-
# FIXME: this is a hack around broken libxml versions
-
return (io << dump_html) if Nokogiri.uses_libxml? && %w[2 6] === LIBXML_VERSION.split('.')[0..1]
-
-
options[:save_with] ||= SaveOptions::DEFAULT_XHTML
-
write_to io, options
-
end
-
-
###
-
# Write Node as XML to +io+ with +options+
-
#
-
# doc.write_xml_to io, :encoding => 'UTF-8'
-
#
-
# See Node#write_to for a list of options
-
1
def write_xml_to io, options = {}
-
options[:save_with] ||= SaveOptions::DEFAULT_XML
-
write_to io, options
-
end
-
-
###
-
# Compare two Node objects with respect to their Document. Nodes from
-
# different documents cannot be compared.
-
1
def <=> other
-
return nil unless other.is_a?(Nokogiri::XML::Node)
-
return nil unless document == other.document
-
compare other
-
end
-
-
###
-
# Do xinclude substitution on the subtree below node. If given a block, a
-
# Nokogiri::XML::ParseOptions object initialized from +options+, will be
-
# passed to it, allowing more convenient modification of the parser options.
-
1
def do_xinclude options = XML::ParseOptions::DEFAULT_XML, &block
-
options = Nokogiri::XML::ParseOptions.new(options) if Fixnum === options
-
-
# give options to user
-
yield options if block_given?
-
-
# call c extension
-
process_xincludes(options.to_i)
-
end
-
-
1
def canonicalize(mode=XML::XML_C14N_1_0,inclusive_namespaces=nil,with_comments=false)
-
c14n_root = self
-
document.canonicalize(mode, inclusive_namespaces, with_comments) do |node, parent|
-
tn = node.is_a?(XML::Node) ? node : parent
-
tn == c14n_root || tn.ancestors.include?(c14n_root)
-
end
-
end
-
-
1
private
-
-
1
def extract_params params # :nodoc:
-
# Pop off our custom function handler if it exists
-
handler = params.find { |param|
-
![Hash, String, Symbol].include?(param.class)
-
}
-
-
params -= [handler] if handler
-
-
hashes = []
-
while Hash === params.last || params.last.nil?
-
hashes << params.pop
-
break if params.empty?
-
end
-
-
ns, binds = hashes.reverse
-
-
ns ||= document.root ? document.root.namespaces : {}
-
-
[params, handler, ns, binds]
-
end
-
-
1
def coerce data # :nodoc:
-
return data if data.is_a?(XML::NodeSet)
-
return data.children if data.is_a?(XML::DocumentFragment)
-
return fragment(data).children if data.is_a?(String)
-
-
if data.is_a?(Document) || data.is_a?(XML::Attr) || !data.is_a?(XML::Node)
-
raise ArgumentError, <<-EOERR
-
Requires a Node, NodeSet or String argument, and cannot accept a #{data.class}.
-
(You probably want to select a node from the Document with at() or search(), or create a new Node via Node.new().)
-
EOERR
-
end
-
-
data
-
end
-
-
1
def implied_xpath_context
-
"./"
-
end
-
-
1
def inspect_attributes
-
[:name, :namespace, :attribute_nodes, :children]
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class Node
-
###
-
# Save options for serializing nodes
-
1
class SaveOptions
-
# Format serialized xml
-
1
FORMAT = 1
-
# Do not include declarations
-
1
NO_DECLARATION = 2
-
# Do not include empty tags
-
1
NO_EMPTY_TAGS = 4
-
# Do not save XHTML
-
1
NO_XHTML = 8
-
# Save as XHTML
-
1
AS_XHTML = 16
-
# Save as XML
-
1
AS_XML = 32
-
# Save as HTML
-
1
AS_HTML = 64
-
-
1
if Nokogiri.jruby?
-
# Save builder created document
-
AS_BUILDER = 128
-
# the default for XML documents
-
DEFAULT_XML = AS_XML # https://github.com/tenderlove/nokogiri/issues/#issue/415
-
# the default for HTML document
-
DEFAULT_HTML = NO_DECLARATION | NO_EMPTY_TAGS | AS_HTML
-
else
-
# the default for XML documents
-
1
DEFAULT_XML = FORMAT | AS_XML
-
# the default for HTML document
-
1
DEFAULT_HTML = FORMAT | NO_DECLARATION | NO_EMPTY_TAGS | AS_HTML
-
end
-
# the default for XHTML document
-
1
DEFAULT_XHTML = FORMAT | NO_DECLARATION | NO_EMPTY_TAGS | AS_XHTML
-
-
# Integer representation of the SaveOptions
-
1
attr_reader :options
-
-
# Create a new SaveOptions object with +options+
-
1
def initialize options = 0; @options = options; end
-
-
1
constants.each do |constant|
-
class_eval %{
-
def #{constant.downcase}
-
@options |= #{constant}
-
self
-
end
-
-
def #{constant.downcase}?
-
#{constant} & @options == #{constant}
-
end
-
10
}
-
end
-
-
1
alias :to_i :options
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
####
-
# A NodeSet contains a list of Nokogiri::XML::Node objects. Typically
-
# a NodeSet is return as a result of searching a Document via
-
# Nokogiri::XML::Node#css or Nokogiri::XML::Node#xpath
-
1
class NodeSet
-
1
include Enumerable
-
-
# The Document this NodeSet is associated with
-
1
attr_accessor :document
-
-
# Create a NodeSet with +document+ defaulting to +list+
-
1
def initialize document, list = []
-
@document = document
-
document.decorate(self)
-
list.each { |x| self << x }
-
yield self if block_given?
-
end
-
-
###
-
# Get the first element of the NodeSet.
-
1
def first n = nil
-
return self[0] unless n
-
list = []
-
0.upto(n - 1) do |i|
-
list << self[i]
-
end
-
list
-
end
-
-
###
-
# Get the last element of the NodeSet.
-
1
def last
-
self[length - 1]
-
end
-
-
###
-
# Is this NodeSet empty?
-
1
def empty?
-
length == 0
-
end
-
-
###
-
# Returns the index of the first node in self that is == to +node+. Returns nil if no match is found.
-
1
def index(node)
-
each_with_index { |member, j| return j if member == node }
-
nil
-
end
-
-
###
-
# Insert +datum+ before the first Node in this NodeSet
-
1
def before datum
-
first.before datum
-
end
-
-
###
-
# Insert +datum+ after the last Node in this NodeSet
-
1
def after datum
-
last.after datum
-
end
-
-
1
alias :<< :push
-
1
alias :remove :unlink
-
-
###
-
# Search this document for +paths+
-
#
-
# For more information see Nokogiri::XML::Node#css and
-
# Nokogiri::XML::Node#xpath
-
1
def search *paths
-
handler = ![
-
Hash, String, Symbol
-
].include?(paths.last.class) ? paths.pop : nil
-
-
ns = paths.last.is_a?(Hash) ? paths.pop : nil
-
-
sub_set = NodeSet.new(document)
-
-
paths.each do |path|
-
sub_set += send(
-
path =~ /^(\.\/|\/)/ ? :xpath : :css,
-
*(paths + [ns, handler]).compact
-
)
-
end
-
-
document.decorate(sub_set)
-
sub_set
-
end
-
1
alias :/ :search
-
-
###
-
# Search this NodeSet for css +paths+
-
#
-
# For more information see Nokogiri::XML::Node#css
-
1
def css *paths
-
handler = ![
-
Hash, String, Symbol
-
].include?(paths.last.class) ? paths.pop : nil
-
-
ns = paths.last.is_a?(Hash) ? paths.pop : nil
-
-
sub_set = NodeSet.new(document)
-
-
each do |node|
-
doc = node.document
-
search_ns = ns || (doc.root ? doc.root.namespaces : {})
-
-
xpaths = paths.map { |rule|
-
[
-
CSS.xpath_for(rule.to_s, :prefix => ".//", :ns => search_ns),
-
CSS.xpath_for(rule.to_s, :prefix => "self::", :ns => search_ns)
-
].join(' | ')
-
}
-
-
sub_set += node.xpath(*(xpaths + [search_ns, handler].compact))
-
end
-
document.decorate(sub_set)
-
sub_set
-
end
-
-
###
-
# Search this NodeSet for XPath +paths+
-
#
-
# For more information see Nokogiri::XML::Node#xpath
-
1
def xpath *paths
-
handler = ![
-
Hash, String, Symbol
-
].include?(paths.last.class) ? paths.pop : nil
-
-
ns = paths.last.is_a?(Hash) ? paths.pop : nil
-
-
sub_set = NodeSet.new(document)
-
each do |node|
-
sub_set += node.xpath(*(paths + [ns, handler].compact))
-
end
-
document.decorate(sub_set)
-
sub_set
-
end
-
-
###
-
# Search this NodeSet's nodes' immediate children using CSS selector +selector+
-
1
def > selector
-
ns = document.root.namespaces
-
xpath CSS.xpath_for(selector, :prefix => "./", :ns => ns).first
-
end
-
-
###
-
# If path is a string, search this document for +path+ returning the
-
# first Node. Otherwise, index in to the array with +path+.
-
1
def at path, ns = document.root ? document.root.namespaces : {}
-
return self[path] if path.is_a?(Numeric)
-
search(path, ns).first
-
end
-
1
alias :% :at
-
-
##
-
# Search this NodeSet for the first occurrence of XPath +paths+.
-
# Equivalent to <tt>xpath(paths).first</tt>
-
# See NodeSet#xpath for more information.
-
#
-
1
def at_xpath *paths
-
xpath(*paths).first
-
end
-
-
##
-
# Search this NodeSet for the first occurrence of CSS +rules+.
-
# Equivalent to <tt>css(rules).first</tt>
-
# See NodeSet#css for more information.
-
#
-
1
def at_css *rules
-
css(*rules).first
-
end
-
-
###
-
# Filter this list for nodes that match +expr+
-
1
def filter expr
-
find_all { |node| node.matches?(expr) }
-
end
-
-
###
-
# Append the class attribute +name+ to all Node objects in the NodeSet.
-
1
def add_class name
-
each do |el|
-
classes = el['class'].to_s.split(/\s+/)
-
el['class'] = classes.push(name).uniq.join " "
-
end
-
self
-
end
-
-
###
-
# Remove the class attribute +name+ from all Node objects in the NodeSet.
-
# If +name+ is nil, remove the class attribute from all Nodes in the
-
# NodeSet.
-
1
def remove_class name = nil
-
each do |el|
-
if name
-
classes = el['class'].to_s.split(/\s+/)
-
if classes.empty?
-
el.delete 'class'
-
else
-
el['class'] = (classes - [name]).uniq.join " "
-
end
-
else
-
el.delete "class"
-
end
-
end
-
self
-
end
-
-
###
-
# Set the attribute +key+ to +value+ or the return value of +blk+
-
# on all Node objects in the NodeSet.
-
1
def attr key, value = nil, &blk
-
unless Hash === key || key && (value || blk)
-
return first.attribute(key)
-
end
-
-
hash = key.is_a?(Hash) ? key : { key => value }
-
-
hash.each { |k,v| each { |el| el[k] = v || blk[el] } }
-
-
self
-
end
-
1
alias :set :attr
-
1
alias :attribute :attr
-
-
###
-
# Remove the attributed named +name+ from all Node objects in the NodeSet
-
1
def remove_attr name
-
each { |el| el.delete name }
-
self
-
end
-
-
###
-
# Iterate over each node, yielding to +block+
-
1
def each(&block)
-
0.upto(length - 1) do |x|
-
yield self[x]
-
end
-
end
-
-
###
-
# Get the inner text of all contained Node objects
-
1
def inner_text
-
collect{|j| j.inner_text}.join('')
-
end
-
1
alias :text :inner_text
-
-
###
-
# Get the inner html of all contained Node objects
-
1
def inner_html *args
-
collect{|j| j.inner_html(*args) }.join('')
-
end
-
-
###
-
# Wrap this NodeSet with +html+ or the results of the builder in +blk+
-
1
def wrap(html, &blk)
-
each do |j|
-
new_parent = document.parse(html).first
-
j.add_next_sibling(new_parent)
-
new_parent.add_child(j)
-
end
-
self
-
end
-
-
###
-
# Convert this NodeSet to a string.
-
1
def to_s
-
map { |x| x.to_s }.join
-
end
-
-
###
-
# Convert this NodeSet to HTML
-
1
def to_html *args
-
if Nokogiri.jruby?
-
options = args.first.is_a?(Hash) ? args.shift : {}
-
if !options[:save_with]
-
options[:save_with] = Node::SaveOptions::NO_DECLARATION | Node::SaveOptions::NO_EMPTY_TAGS | Node::SaveOptions::AS_HTML
-
end
-
args.insert(0, options)
-
end
-
map { |x| x.to_html(*args) }.join
-
end
-
-
###
-
# Convert this NodeSet to XHTML
-
1
def to_xhtml *args
-
map { |x| x.to_xhtml(*args) }.join
-
end
-
-
###
-
# Convert this NodeSet to XML
-
1
def to_xml *args
-
map { |x| x.to_xml(*args) }.join
-
end
-
-
1
alias :size :length
-
1
alias :to_ary :to_a
-
-
###
-
# Removes the last element from set and returns it, or +nil+ if
-
# the set is empty
-
1
def pop
-
return nil if length == 0
-
delete last
-
end
-
-
###
-
# Returns the first element of the NodeSet and removes it. Returns
-
# +nil+ if the set is empty.
-
1
def shift
-
return nil if length == 0
-
delete first
-
end
-
-
###
-
# Equality -- Two NodeSets are equal if the contain the same number
-
# of elements and if each element is equal to the corresponding
-
# element in the other NodeSet
-
1
def == other
-
return false unless other.is_a?(Nokogiri::XML::NodeSet)
-
return false unless length == other.length
-
each_with_index do |node, i|
-
return false unless node == other[i]
-
end
-
true
-
end
-
-
###
-
# Returns a new NodeSet containing all the children of all the nodes in
-
# the NodeSet
-
1
def children
-
inject(NodeSet.new(document)) { |set, node| set += node.children }
-
end
-
-
###
-
# Returns a new NodeSet containing all the nodes in the NodeSet
-
# in reverse order
-
1
def reverse
-
node_set = NodeSet.new(document)
-
(length - 1).downto(0) do |x|
-
node_set.push self[x]
-
end
-
node_set
-
end
-
-
###
-
# Return a nicely formated string representation
-
1
def inspect
-
"[#{map { |c| c.inspect }.join ', '}]"
-
end
-
-
1
alias :+ :|
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class Notation < Struct.new(:name, :public_id, :system_id)
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
###
-
# Parse options for passing to Nokogiri.XML or Nokogiri.HTML
-
1
class ParseOptions
-
# Strict parsing
-
1
STRICT = 0
-
# Recover from errors
-
1
RECOVER = 1 << 0
-
# Substitute entities
-
1
NOENT = 1 << 1
-
# Load external subsets
-
1
DTDLOAD = 1 << 2
-
# Default DTD attributes
-
1
DTDATTR = 1 << 3
-
# validate with the DTD
-
1
DTDVALID = 1 << 4
-
# suppress error reports
-
1
NOERROR = 1 << 5
-
# suppress warning reports
-
1
NOWARNING = 1 << 6
-
# pedantic error reporting
-
1
PEDANTIC = 1 << 7
-
# remove blank nodes
-
1
NOBLANKS = 1 << 8
-
# use the SAX1 interface internally
-
1
SAX1 = 1 << 9
-
# Implement XInclude substitition
-
1
XINCLUDE = 1 << 10
-
# Forbid network access
-
1
NONET = 1 << 11
-
# Do not reuse the context dictionnary
-
1
NODICT = 1 << 12
-
# remove redundant namespaces declarations
-
1
NSCLEAN = 1 << 13
-
# merge CDATA as text nodes
-
1
NOCDATA = 1 << 14
-
# do not generate XINCLUDE START/END nodes
-
1
NOXINCNODE = 1 << 15
-
# compact small text nodes; no modification of the tree allowed afterwards (will possibly crash if you try to modify the tree)
-
1
COMPACT = 1 << 16
-
# parse using XML-1.0 before update 5
-
1
OLD10 = 1 << 17
-
# do not fixup XINCLUDE xml:base uris
-
1
NOBASEFIX = 1 << 18
-
# relax any hardcoded limit from the parser
-
1
HUGE = 1 << 19
-
-
# the default options used for parsing XML documents
-
1
DEFAULT_XML = RECOVER
-
# the default options used for parsing HTML documents
-
1
DEFAULT_HTML = RECOVER | NOERROR | NOWARNING | NONET
-
-
1
attr_accessor :options
-
1
def initialize options = STRICT
-
@options = options
-
end
-
-
1
constants.each do |constant|
-
23
next if constant.to_sym == :STRICT
-
class_eval %{
-
def #{constant.downcase}
-
@options |= #{constant}
-
self
-
end
-
-
def #{constant.downcase}?
-
#{constant} & @options == #{constant}
-
end
-
22
}
-
end
-
-
1
def strict
-
@options &= ~RECOVER
-
self
-
end
-
-
1
def strict?
-
@options & RECOVER == STRICT
-
end
-
-
1
alias :to_i :options
-
-
1
def inspect
-
options = []
-
self.class.constants.each do |k|
-
options << k.downcase if send(:"#{k.downcase}?")
-
end
-
super.sub(/>$/, " " + options.join(', ') + ">")
-
end
-
end
-
end
-
end
-
1
require 'nokogiri/xml/pp/node'
-
1
require 'nokogiri/xml/pp/character_data'
-
1
module Nokogiri
-
1
module XML
-
1
module PP
-
1
module CharacterData
-
1
def pretty_print pp # :nodoc:
-
nice_name = self.class.name.split('::').last
-
pp.group(2, "#(#{nice_name} ", ')') do
-
pp.pp text
-
end
-
end
-
-
1
def inspect # :nodoc:
-
"#<#{self.class.name}:#{sprintf("0x%x",object_id)} #{text.inspect}>"
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
module PP
-
1
module Node
-
1
def inspect # :nodoc:
-
attributes = inspect_attributes.reject { |x|
-
begin
-
attribute = send x
-
!attribute || (attribute.respond_to?(:empty?) && attribute.empty?)
-
rescue NoMethodError
-
true
-
end
-
}.map { |attribute|
-
"#{attribute.to_s.sub(/_\w+/, 's')}=#{send(attribute).inspect}"
-
}.join ' '
-
"#<#{self.class.name}:#{sprintf("0x%x", object_id)} #{attributes}>"
-
end
-
-
1
def pretty_print pp # :nodoc:
-
nice_name = self.class.name.split('::').last
-
pp.group(2, "#(#{nice_name}:#{sprintf("0x%x", object_id)} {", '})') do
-
-
pp.breakable
-
attrs = inspect_attributes.map { |t|
-
[t, send(t)] if respond_to?(t)
-
}.compact.find_all { |x|
-
if x.last
-
if [:attribute_nodes, :children].include? x.first
-
!x.last.empty?
-
else
-
true
-
end
-
end
-
}
-
-
pp.seplist(attrs) do |v|
-
if [:attribute_nodes, :children].include? v.first
-
pp.group(2, "#{v.first.to_s.sub(/_\w+$/, 's')} = [", "]") do
-
pp.breakable
-
pp.seplist(v.last) do |item|
-
pp.pp item
-
end
-
end
-
else
-
pp.text "#{v.first} = "
-
pp.pp v.last
-
end
-
end
-
pp.breakable
-
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class ProcessingInstruction < Node
-
1
def initialize document, name, content
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
###
-
# Nokogiri::XML::Reader parses an XML document similar to the way a cursor
-
# would move. The Reader is given an XML document, and yields nodes
-
# to an each block.
-
#
-
# Here is an example of usage:
-
#
-
# reader = Nokogiri::XML::Reader(<<-eoxml)
-
# <x xmlns:tenderlove='http://tenderlovemaking.com/'>
-
# <tenderlove:foo awesome='true'>snuggles!</tenderlove:foo>
-
# </x>
-
# eoxml
-
#
-
# reader.each do |node|
-
#
-
# # node is an instance of Nokogiri::XML::Reader
-
# puts node.name
-
#
-
# end
-
#
-
# Note that Nokogiri::XML::Reader#each can only be called once!! Once
-
# the cursor moves through the entire document, you must parse the
-
# document again. So make sure that you capture any information you
-
# need during the first iteration.
-
#
-
# The Reader parser is good for when you need the speed of a SAX parser,
-
# but do not want to write a Document handler.
-
1
class Reader
-
1
include Enumerable
-
-
1
TYPE_NONE = 0
-
# Element node type
-
1
TYPE_ELEMENT = 1
-
# Attribute node type
-
1
TYPE_ATTRIBUTE = 2
-
# Text node type
-
1
TYPE_TEXT = 3
-
# CDATA node type
-
1
TYPE_CDATA = 4
-
# Entity Reference node type
-
1
TYPE_ENTITY_REFERENCE = 5
-
# Entity node type
-
1
TYPE_ENTITY = 6
-
# PI node type
-
1
TYPE_PROCESSING_INSTRUCTION = 7
-
# Comment node type
-
1
TYPE_COMMENT = 8
-
# Document node type
-
1
TYPE_DOCUMENT = 9
-
# Document Type node type
-
1
TYPE_DOCUMENT_TYPE = 10
-
# Document Fragment node type
-
1
TYPE_DOCUMENT_FRAGMENT = 11
-
# Notation node type
-
1
TYPE_NOTATION = 12
-
# Whitespace node type
-
1
TYPE_WHITESPACE = 13
-
# Significant Whitespace node type
-
1
TYPE_SIGNIFICANT_WHITESPACE = 14
-
# Element end node type
-
1
TYPE_END_ELEMENT = 15
-
# Entity end node type
-
1
TYPE_END_ENTITY = 16
-
# XML Declaration node type
-
1
TYPE_XML_DECLARATION = 17
-
-
# A list of errors encountered while parsing
-
1
attr_accessor :errors
-
-
# The encoding for the document
-
1
attr_reader :encoding
-
-
# The XML source
-
1
attr_reader :source
-
-
1
alias :self_closing? :empty_element?
-
-
1
def initialize source, url = nil, encoding = nil # :nodoc:
-
@source = source
-
@errors = []
-
@encoding = encoding
-
end
-
1
private :initialize
-
-
###
-
# Get a list of attributes for the current node.
-
1
def attributes
-
Hash[attribute_nodes.map { |node|
-
[node.name, node.to_s]
-
}].merge(namespaces || {})
-
end
-
-
###
-
# Get a list of attributes for the current node
-
1
def attribute_nodes
-
nodes = attr_nodes
-
nodes.each { |v| v.instance_variable_set(:@_r, self) }
-
nodes
-
end
-
-
###
-
# Move the cursor through the document yielding the cursor to the block
-
1
def each
-
while cursor = self.read
-
yield cursor
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class << self
-
###
-
# Create a new Nokogiri::XML::RelaxNG document from +string_or_io+.
-
# See Nokogiri::XML::RelaxNG for an example.
-
1
def RelaxNG string_or_io
-
RelaxNG.new(string_or_io)
-
end
-
end
-
-
###
-
# Nokogiri::XML::RelaxNG is used for validating XML against a
-
# RelaxNG schema.
-
#
-
# == Synopsis
-
#
-
# Validate an XML document against a RelaxNG schema. Loop over the errors
-
# that are returned and print them out:
-
#
-
# schema = Nokogiri::XML::RelaxNG(File.open(ADDRESS_SCHEMA_FILE))
-
# doc = Nokogiri::XML(File.open(ADDRESS_XML_FILE))
-
#
-
# schema.validate(doc).each do |error|
-
# puts error.message
-
# end
-
#
-
# The list of errors are Nokogiri::XML::SyntaxError objects.
-
1
class RelaxNG < Nokogiri::XML::Schema
-
end
-
end
-
end
-
1
require 'nokogiri/xml/sax/document'
-
1
require 'nokogiri/xml/sax/parser_context'
-
1
require 'nokogiri/xml/sax/parser'
-
1
require 'nokogiri/xml/sax/push_parser'
-
1
module Nokogiri
-
1
module XML
-
###
-
# SAX Parsers are event driven parsers. Nokogiri provides two different
-
# event based parsers when dealing with XML. If you want to do SAX style
-
# parsing using HTML, check out Nokogiri::HTML::SAX.
-
#
-
# The basic way a SAX style parser works is by creating a parser,
-
# telling the parser about the events we're interested in, then giving
-
# the parser some XML to process. The parser will notify you when
-
# it encounters events your said you would like to know about.
-
#
-
# To register for events, you simply subclass Nokogiri::XML::SAX::Document,
-
# and implement the methods for which you would like notification.
-
#
-
# For example, if I want to be notified when a document ends, and when an
-
# element starts, I would write a class like this:
-
#
-
# class MyDocument < Nokogiri::XML::SAX::Document
-
# def end_document
-
# puts "the document has ended"
-
# end
-
#
-
# def start_element name, attributes = []
-
# puts "#{name} started"
-
# end
-
# end
-
#
-
# Then I would instantiate a SAX parser with this document, and feed the
-
# parser some XML
-
#
-
# # Create a new parser
-
# parser = Nokogiri::XML::SAX::Parser.new(MyDocument.new)
-
#
-
# # Feed the parser some XML
-
# parser.parse(File.read(ARGV[0], 'rb'))
-
#
-
# Now my document handler will be called when each node starts, and when
-
# then document ends. To see what kinds of events are available, take
-
# a look at Nokogiri::XML::SAX::Document.
-
#
-
# Two SAX parsers for XML are available, a parser that reads from a string
-
# or IO object as it feels necessary, and a parser that lets you spoon
-
# feed it XML. If you want to let Nokogiri deal with reading your XML,
-
# use the Nokogiri::XML::SAX::Parser. If you want to have fine grain
-
# control over the XML input, use the Nokogiri::XML::SAX::PushParser.
-
1
module SAX
-
###
-
# This class is used for registering types of events you are interested
-
# in handling. All of the methods on this class are available as
-
# possible events while parsing an XML document. To register for any
-
# particular event, just subclass this class and implement the methods
-
# you are interested in knowing about.
-
#
-
# To only be notified about start and end element events, write a class
-
# like this:
-
#
-
# class MyDocument < Nokogiri::XML::SAX::Document
-
# def start_element name, attrs = []
-
# puts "#{name} started!"
-
# end
-
#
-
# def end_element name
-
# puts "#{name} ended"
-
# end
-
# end
-
#
-
# You can use this event handler for any SAX style parser included with
-
# Nokogiri. See Nokogiri::XML::SAX, and Nokogiri::HTML::SAX.
-
1
class Document
-
###
-
# Called when an XML declaration is parsed
-
1
def xmldecl version, encoding, standalone
-
end
-
-
###
-
# Called when document starts parsing
-
1
def start_document
-
end
-
-
###
-
# Called when document ends parsing
-
1
def end_document
-
end
-
-
###
-
# Called at the beginning of an element
-
# * +name+ is the name of the tag
-
# * +attrs+ are an assoc list of namespaces and attributes, e.g.:
-
# [ ["xmlns:foo", "http://sample.net"], ["size", "large"] ]
-
1
def start_element name, attrs = []
-
end
-
-
###
-
# Called at the end of an element
-
# +name+ is the tag name
-
1
def end_element name
-
end
-
-
###
-
# Called at the beginning of an element
-
# +name+ is the element name
-
# +attrs+ is a list of attributes
-
# +prefix+ is the namespace prefix for the element
-
# +uri+ is the associated namespace URI
-
# +ns+ is a hash of namespace prefix:urls associated with the element
-
1
def start_element_namespace name, attrs = [], prefix = nil, uri = nil, ns = []
-
###
-
# Deal with SAX v1 interface
-
name = [prefix, name].compact.join(':')
-
attributes = ns.map { |ns_prefix,ns_uri|
-
[['xmlns', ns_prefix].compact.join(':'), ns_uri]
-
} + attrs.map { |attr|
-
[[attr.prefix, attr.localname].compact.join(':'), attr.value]
-
}
-
start_element name, attributes
-
end
-
-
###
-
# Called at the end of an element
-
# +name+ is the element's name
-
# +prefix+ is the namespace prefix associated with the element
-
# +uri+ is the associated namespace URI
-
1
def end_element_namespace name, prefix = nil, uri = nil
-
###
-
# Deal with SAX v1 interface
-
end_element [prefix, name].compact.join(':')
-
end
-
-
###
-
# Characters read between a tag. This method might be called multiple
-
# times given one contiguous string of characters.
-
#
-
# +string+ contains the character data
-
1
def characters string
-
end
-
-
###
-
# Called when comments are encountered
-
# +string+ contains the comment data
-
1
def comment string
-
end
-
-
###
-
# Called on document warnings
-
# +string+ contains the warning
-
1
def warning string
-
end
-
-
###
-
# Called on document errors
-
# +string+ contains the error
-
1
def error string
-
end
-
-
###
-
# Called when cdata blocks are found
-
# +string+ contains the cdata content
-
1
def cdata_block string
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
module SAX
-
###
-
# This parser is a SAX style parser that reads it's input as it
-
# deems necessary. The parser takes a Nokogiri::XML::SAX::Document,
-
# an optional encoding, then given an XML input, sends messages to
-
# the Nokogiri::XML::SAX::Document.
-
#
-
# Here is an example of using this parser:
-
#
-
# # Create a subclass of Nokogiri::XML::SAX::Document and implement
-
# # the events we care about:
-
# class MyDoc < Nokogiri::XML::SAX::Document
-
# def start_element name, attrs = []
-
# puts "starting: #{name}"
-
# end
-
#
-
# def end_element name
-
# puts "ending: #{name}"
-
# end
-
# end
-
#
-
# # Create our parser
-
# parser = Nokogiri::XML::SAX::Parser.new(MyDoc.new)
-
#
-
# # Send some XML to the parser
-
# parser.parse(File.read(ARGV[0]))
-
#
-
# For more information about SAX parsers, see Nokogiri::XML::SAX. Also
-
# see Nokogiri::XML::SAX::Document for the available events.
-
1
class Parser
-
1
class Attribute < Struct.new(:localname, :prefix, :uri, :value)
-
end
-
-
# Encodinds this parser supports
-
1
ENCODINGS = {
-
'NONE' => 0, # No char encoding detected
-
'UTF-8' => 1, # UTF-8
-
'UTF16LE' => 2, # UTF-16 little endian
-
'UTF16BE' => 3, # UTF-16 big endian
-
'UCS4LE' => 4, # UCS-4 little endian
-
'UCS4BE' => 5, # UCS-4 big endian
-
'EBCDIC' => 6, # EBCDIC uh!
-
'UCS4-2143' => 7, # UCS-4 unusual ordering
-
'UCS4-3412' => 8, # UCS-4 unusual ordering
-
'UCS2' => 9, # UCS-2
-
'ISO-8859-1' => 10, # ISO-8859-1 ISO Latin 1
-
'ISO-8859-2' => 11, # ISO-8859-2 ISO Latin 2
-
'ISO-8859-3' => 12, # ISO-8859-3
-
'ISO-8859-4' => 13, # ISO-8859-4
-
'ISO-8859-5' => 14, # ISO-8859-5
-
'ISO-8859-6' => 15, # ISO-8859-6
-
'ISO-8859-7' => 16, # ISO-8859-7
-
'ISO-8859-8' => 17, # ISO-8859-8
-
'ISO-8859-9' => 18, # ISO-8859-9
-
'ISO-2022-JP' => 19, # ISO-2022-JP
-
'SHIFT-JIS' => 20, # Shift_JIS
-
'EUC-JP' => 21, # EUC-JP
-
'ASCII' => 22, # pure ASCII
-
}
-
-
# The Nokogiri::XML::SAX::Document where events will be sent.
-
1
attr_accessor :document
-
-
# The encoding beings used for this document.
-
1
attr_accessor :encoding
-
-
# Create a new Parser with +doc+ and +encoding+
-
1
def initialize doc = Nokogiri::XML::SAX::Document.new, encoding = 'UTF-8'
-
@encoding = encoding
-
@document = doc
-
@warned = false
-
end
-
-
###
-
# Parse given +thing+ which may be a string containing xml, or an
-
# IO object.
-
1
def parse thing, &block
-
if thing.respond_to?(:read) && thing.respond_to?(:close)
-
parse_io(thing, &block)
-
else
-
parse_memory(thing, &block)
-
end
-
end
-
-
###
-
# Parse given +io+
-
1
def parse_io io, encoding = 'ASCII'
-
@encoding = encoding
-
ctx = ParserContext.io(io, ENCODINGS[encoding])
-
yield ctx if block_given?
-
ctx.parse_with self
-
end
-
-
###
-
# Parse a file with +filename+
-
1
def parse_file filename
-
raise ArgumentError unless filename
-
raise Errno::ENOENT unless File.exists?(filename)
-
raise Errno::EISDIR if File.directory?(filename)
-
ctx = ParserContext.file filename
-
yield ctx if block_given?
-
ctx.parse_with self
-
end
-
-
1
def parse_memory data
-
ctx = ParserContext.memory data
-
yield ctx if block_given?
-
ctx.parse_with self
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
module SAX
-
###
-
# Context for XML SAX parsers. This class is usually not instantiated
-
# by the user. Instead, you should be looking at
-
# Nokogiri::XML::SAX::Parser
-
1
class ParserContext
-
1
def self.new thing, encoding = 'UTF-8'
-
[:read, :close].all? { |x| thing.respond_to?(x) } ?
-
io(thing, Parser::ENCODINGS[encoding]) : memory(thing)
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
module SAX
-
###
-
# PushParser can parse a document that is fed to it manually. It
-
# must be given a SAX::Document object which will be called with
-
# SAX events as the document is being parsed.
-
#
-
# Calling PushParser#<< writes XML to the parser, calling any SAX
-
# callbacks it can.
-
#
-
# PushParser#finish tells the parser that the document is finished
-
# and calls the end_document SAX method.
-
#
-
# Example:
-
#
-
# parser = PushParser.new(Class.new(XML::SAX::Document) {
-
# def start_document
-
# puts "start document called"
-
# end
-
# }.new)
-
# parser << "<div>hello<"
-
# parser << "/div>"
-
# parser.finish
-
1
class PushParser
-
-
# The Nokogiri::XML::SAX::Document on which the PushParser will be
-
# operating
-
1
attr_accessor :document
-
-
###
-
# Create a new PushParser with +doc+ as the SAX Document, providing
-
# an optional +file_name+ and +encoding+
-
1
def initialize(doc = XML::SAX::Document.new, file_name = nil, encoding = 'UTF-8')
-
@document = doc
-
@encoding = encoding
-
@sax_parser = XML::SAX::Parser.new(doc)
-
-
## Create our push parser context
-
initialize_native(@sax_parser, file_name)
-
end
-
-
###
-
# Write a +chunk+ of XML to the PushParser. Any callback methods
-
# that can be called will be called immediately.
-
1
def write chunk, last_chunk = false
-
native_write(chunk, last_chunk)
-
end
-
1
alias :<< :write
-
-
###
-
# Finish the parsing. This method is only necessary for
-
# Nokogiri::XML::SAX::Document#end_document to be called.
-
1
def finish
-
write '', true
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class << self
-
###
-
# Create a new Nokogiri::XML::Schema object using a +string_or_io+
-
# object.
-
1
def Schema string_or_io
-
Schema.new(string_or_io)
-
end
-
end
-
-
###
-
# Nokogiri::XML::Schema is used for validating XML against a schema
-
# (usually from an xsd file).
-
#
-
# == Synopsis
-
#
-
# Validate an XML document against a Schema. Loop over the errors that
-
# are returned and print them out:
-
#
-
# xsd = Nokogiri::XML::Schema(File.read(PO_SCHEMA_FILE))
-
# doc = Nokogiri::XML(File.read(PO_XML_FILE))
-
#
-
# xsd.validate(doc).each do |error|
-
# puts error.message
-
# end
-
#
-
# The list of errors are Nokogiri::XML::SyntaxError objects.
-
1
class Schema
-
# Errors while parsing the schema file
-
1
attr_accessor :errors
-
-
###
-
# Create a new Nokogiri::XML::Schema object using a +string_or_io+
-
# object.
-
1
def self.new string_or_io
-
from_document Nokogiri::XML(string_or_io)
-
end
-
-
###
-
# Validate +thing+ against this schema. +thing+ can be a
-
# Nokogiri::XML::Document object, or a filename. An Array of
-
# Nokogiri::XML::SyntaxError objects found while validating the
-
# +thing+ is returned.
-
1
def validate thing
-
if thing.is_a?(Nokogiri::XML::Document)
-
validate_document(thing)
-
elsif File.file?(thing)
-
validate_file(thing)
-
else
-
raise ArgumentError, "Must provide Nokogiri::Xml::Document or the name of an existing file"
-
end
-
end
-
-
###
-
# Returns true if +thing+ is a valid Nokogiri::XML::Document or
-
# file.
-
1
def valid? thing
-
validate(thing).length == 0
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
###
-
# This class provides information about XML SyntaxErrors. These
-
# exceptions are typically stored on Nokogiri::XML::Document#errors.
-
1
class SyntaxError < ::Nokogiri::SyntaxError
-
1
attr_reader :domain
-
1
attr_reader :code
-
1
attr_reader :level
-
1
attr_reader :file
-
1
attr_reader :line
-
1
attr_reader :str1
-
1
attr_reader :str2
-
1
attr_reader :str3
-
1
attr_reader :int1
-
1
attr_reader :column
-
-
###
-
# return true if this is a non error
-
1
def none?
-
level == 0
-
end
-
-
###
-
# return true if this is a warning
-
1
def warning?
-
level == 1
-
end
-
-
###
-
# return true if this is an error
-
1
def error?
-
level == 2
-
end
-
-
###
-
# return true if this error is fatal
-
1
def fatal?
-
level == 3
-
end
-
-
1
def to_s
-
super.chomp
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class Text < Nokogiri::XML::CharacterData
-
1
def content=(string)
-
self.native_content = string.to_s
-
end
-
end
-
end
-
end
-
1
require 'nokogiri/xml/xpath/syntax_error'
-
-
1
module Nokogiri
-
1
module XML
-
1
class XPath
-
# The Nokogiri::XML::Document tied to this XPath instance
-
1
attr_accessor :document
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class XPath
-
1
class SyntaxError < XML::SyntaxError
-
1
def to_s
-
[super.chomp, str1].compact.join(': ')
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class XPathContext
-
-
###
-
# Register namespaces in +namespaces+
-
1
def register_namespaces(namespaces)
-
namespaces.each do |k, v|
-
k = k.gsub(/.*:/,'') # strip off 'xmlns:' or 'xml:'
-
register_ns(k, v)
-
end
-
end
-
-
end
-
end
-
end
-
1
require 'nokogiri/xslt/stylesheet'
-
-
1
module Nokogiri
-
1
class << self
-
###
-
# Create a Nokogiri::XSLT::Stylesheet with +stylesheet+.
-
#
-
# Example:
-
#
-
# xslt = Nokogiri::XSLT(File.read(ARGV[0]))
-
#
-
1
def XSLT stylesheet, modules = {}
-
XSLT.parse(stylesheet, modules)
-
end
-
end
-
-
###
-
# See Nokogiri::XSLT::Stylesheet for creating and maniuplating
-
# Stylesheet object.
-
1
module XSLT
-
1
class << self
-
###
-
# Parse the stylesheet in +string+, register any +modules+
-
1
def parse string, modules = {}
-
modules.each do |url, klass|
-
XSLT.register url, klass
-
end
-
-
if Nokogiri.jruby?
-
Stylesheet.parse_stylesheet_doc(XML.parse(string), string)
-
else
-
Stylesheet.parse_stylesheet_doc(XML.parse(string))
-
end
-
end
-
-
###
-
# Quote parameters in +params+ for stylesheet safety
-
1
def quote_params params
-
parray = (params.instance_of?(Hash) ? params.to_a.flatten : params).dup
-
parray.each_with_index do |v,i|
-
if i % 2 > 0
-
parray[i]=
-
if v =~ /'/
-
"concat('#{ v.gsub(/'/, %q{', "'", '}) }')"
-
else
-
"'#{v}'";
-
end
-
else
-
parray[i] = v.to_s
-
end
-
end
-
parray.flatten
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XSLT
-
###
-
# A Stylesheet represents an XSLT Stylesheet object. Stylesheet creation
-
# is done through Nokogiri.XSLT. Here is an example of transforming
-
# an XML::Document with a Stylesheet:
-
#
-
# doc = Nokogiri::XML(File.read('some_file.xml'))
-
# xslt = Nokogiri::XSLT(File.read('some_transformer.xslt'))
-
#
-
# puts xslt.transform(doc)
-
#
-
# See Nokogiri::XSLT::Stylesheet#transform for more transformation
-
# information.
-
1
class Stylesheet
-
###
-
# Apply an XSLT stylesheet to an XML::Document.
-
# +params+ is an array of strings used as XSLT parameters.
-
# returns serialized document
-
1
def apply_to document, params = []
-
serialize(transform(document, params))
-
end
-
end
-
end
-
end
-
# :stopdoc:
-
-
# Stolen from ruby core's uri/common.rb @32618ba to fix DoS issues in 1.9.2
-
#
-
# https://github.com/ruby/ruby/blob/32618ba7438a2247042bba9b5d85b5d49070f5e5/lib/uri/common.rb
-
#
-
# Issue:
-
# http://redmine.ruby-lang.org/issues/5149
-
#
-
# Relevant Fixes:
-
# https://github.com/ruby/ruby/commit/b5f91deee04aa6ccbe07c23c8222b937c22a799b
-
# https://github.com/ruby/ruby/commit/93177c1e5c3906abf14472ae0b905d8b5c72ce1b
-
#
-
# This should probably be removed once there is a Ruby 1.9.2 patch level that
-
# includes this fix.
-
-
1
require 'uri/common'
-
-
1
module URI
-
1
def self.decode_www_form(str, enc=Encoding::UTF_8)
-
return [] if str.empty?
-
unless /\A#{WFKV_}=#{WFKV_}(?:[;&]#{WFKV_}=#{WFKV_})*\z/o =~ str
-
raise ArgumentError, "invalid data of application/x-www-form-urlencoded (#{str})"
-
end
-
ary = []
-
$&.scan(/([^=;&]+)=([^;&]*)/) do
-
ary << [decode_www_form_component($1, enc), decode_www_form_component($2, enc)]
-
end
-
ary
-
end
-
-
1
def self.decode_www_form_component(str, enc=Encoding::UTF_8)
-
if TBLDECWWWCOMP_.empty?
-
tbl = {}
-
256.times do |i|
-
h, l = i>>4, i&15
-
tbl['%%%X%X' % [h, l]] = i.chr
-
tbl['%%%x%X' % [h, l]] = i.chr
-
tbl['%%%X%x' % [h, l]] = i.chr
-
tbl['%%%x%x' % [h, l]] = i.chr
-
end
-
tbl['+'] = ' '
-
begin
-
TBLDECWWWCOMP_.replace(tbl)
-
TBLDECWWWCOMP_.freeze
-
rescue
-
end
-
end
-
raise ArgumentError, "invalid %-encoding (#{str})" unless /\A[^%]*(?:%\h\h[^%]*)*\z/ =~ str
-
str.gsub(/\+|%\h\h/, TBLDECWWWCOMP_).force_encoding(enc)
-
end
-
-
1
remove_const :WFKV_
-
1
WFKV_ = '(?:[^%#=;&]*(?:%\h\h[^%#=;&]*)*)' # :nodoc:
-
end
-
1
module Rack
-
1
class BodyProxy
-
1
def initialize(body, &block)
-
@body, @block, @closed = body, block, false
-
end
-
-
1
def respond_to?(*args)
-
super or @body.respond_to?(*args)
-
end
-
-
1
def close
-
return if @closed
-
@closed = true
-
@body.close if @body.respond_to? :close
-
@block.call
-
end
-
-
1
def closed?
-
@closed
-
end
-
-
1
def method_missing(*args, &block)
-
@body.__send__(*args, &block)
-
end
-
end
-
end
-
1
module Rack
-
# Rack::Builder implements a small DSL to iteratively construct Rack
-
# applications.
-
#
-
# Example:
-
#
-
# require 'rack/lobster'
-
# app = Rack::Builder.new do
-
# use Rack::CommonLogger
-
# use Rack::ShowExceptions
-
# map "/lobster" do
-
# use Rack::Lint
-
# run Rack::Lobster.new
-
# end
-
# end
-
#
-
# run app
-
#
-
# Or
-
#
-
# app = Rack::Builder.app do
-
# use Rack::CommonLogger
-
# lambda { |env| [200, {'Content-Type' => 'text/plain'}, 'OK'] }
-
# end
-
#
-
# run app
-
#
-
# +use+ adds a middleware to the stack, +run+ dispatches to an application.
-
# You can use +map+ to construct a Rack::URLMap in a convenient way.
-
-
1
class Builder
-
1
def self.parse_file(config, opts = Server::Options.new)
-
options = {}
-
if config =~ /\.ru$/
-
cfgfile = ::File.read(config)
-
if cfgfile[/^#\\(.*)/] && opts
-
options = opts.parse! $1.split(/\s+/)
-
end
-
cfgfile.sub!(/^__END__\n.*/, '')
-
app = eval "Rack::Builder.new {\n" + cfgfile + "\n}.to_app",
-
TOPLEVEL_BINDING, config
-
else
-
require config
-
app = Object.const_get(::File.basename(config, '.rb').capitalize)
-
end
-
return app, options
-
end
-
-
1
def initialize(&block)
-
2
@ins = []
-
2
instance_eval(&block) if block_given?
-
end
-
-
1
def self.app(&block)
-
self.new(&block).to_app
-
end
-
-
# Specifies a middleware to use in a stack.
-
#
-
# class Middleware
-
# def initialize(app)
-
# @app = app
-
# end
-
#
-
# def call(env)
-
# env["rack.some_header"] = "setting an example"
-
# @app.call(env)
-
# end
-
# end
-
#
-
# use Middleware
-
# run lambda { |env| [200, { "Content-Type => "text/plain" }, ["OK"]] }
-
#
-
# All requests through to this application will first be processed by the middleware class.
-
# The +call+ method in this example sets an additional environment key which then can be
-
# referenced in the application if required.
-
1
def use(middleware, *args, &block)
-
@ins << lambda { |app| middleware.new(app, *args, &block) }
-
end
-
-
# Takes an argument that is an object that responds to #call and returns a Rack response.
-
# The simplest form of this is a lambda object:
-
#
-
# run lambda { |env| [200, { "Content-Type" => "text/plain" }, ["OK"]] }
-
#
-
# However this could also be a class:
-
#
-
# class Heartbeat
-
# def self.call(env)
-
# [200, { "Content-Type" => "text/plain" }, ["OK"]]
-
# end
-
# end
-
#
-
# run Heartbeat
-
1
def run(app)
-
1
@ins << app #lambda { |nothing| app }
-
end
-
-
# Creates a route within the application.
-
#
-
# Rack::Builder.app do
-
# map '/' do
-
# run Heartbeat
-
# end
-
# end
-
#
-
# The +use+ method can also be used here to specify middleware to run under a specific path:
-
#
-
# Rack::Builder.app do
-
# map '/' do
-
# use Middleware
-
# run Heartbeat
-
# end
-
# end
-
#
-
# This example includes a piece of middleware which will run before requests hit +Heartbeat+.
-
#
-
1
def map(path, &block)
-
2
if @ins.last.kind_of? Hash
-
1
@ins.last[path] = self.class.new(&block).to_app
-
else
-
1
@ins << {}
-
1
map(path, &block)
-
end
-
end
-
-
1
def to_app
-
2
@ins[-1] = Rack::URLMap.new(@ins.last) if Hash === @ins.last
-
2
inner_app = @ins.last
-
2
@ins[0...-1].reverse.inject(inner_app) { |a, e| e.call(a) }
-
end
-
-
1
def call(env)
-
to_app.call(env)
-
end
-
end
-
end
-
1
require 'rack/utils'
-
-
1
module Rack
-
-
# Middleware that applies chunked transfer encoding to response bodies
-
# when the response does not include a Content-Length header.
-
1
class Chunked
-
1
include Rack::Utils
-
-
# A body wrapper that emits chunked responses
-
1
class Body
-
1
TERM = "\r\n"
-
1
TAIL = "0#{TERM}#{TERM}"
-
-
1
include Rack::Utils
-
-
1
def initialize(body)
-
@body = body
-
end
-
-
1
def each
-
term = TERM
-
@body.each do |chunk|
-
size = bytesize(chunk)
-
next if size == 0
-
yield [size.to_s(16), term, chunk, term].join
-
end
-
yield TAIL
-
end
-
-
1
def close
-
@body.close if @body.respond_to?(:close)
-
end
-
end
-
-
1
def initialize(app)
-
@app = app
-
end
-
-
1
def call(env)
-
status, headers, body = @app.call(env)
-
headers = HeaderHash.new(headers)
-
-
if env['HTTP_VERSION'] == 'HTTP/1.0' ||
-
STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
-
headers['Content-Length'] ||
-
headers['Transfer-Encoding']
-
[status, headers, body]
-
else
-
headers.delete('Content-Length')
-
headers['Transfer-Encoding'] = 'chunked'
-
[status, headers, Body.new(body)]
-
end
-
end
-
end
-
end
-
1
require 'rack/utils'
-
-
1
module Rack
-
-
# Middleware that enables conditional GET using If-None-Match and
-
# If-Modified-Since. The application should set either or both of the
-
# Last-Modified or Etag response headers according to RFC 2616. When
-
# either of the conditions is met, the response body is set to be zero
-
# length and the response status is set to 304 Not Modified.
-
#
-
# Applications that defer response body generation until the body's each
-
# message is received will avoid response body generation completely when
-
# a conditional GET matches.
-
#
-
# Adapted from Michael Klishin's Merb implementation:
-
# http://github.com/wycats/merb-core/tree/master/lib/merb-core/rack/middleware/conditional_get.rb
-
1
class ConditionalGet
-
1
def initialize(app)
-
1
@app = app
-
end
-
-
1
def call(env)
-
case env['REQUEST_METHOD']
-
when "GET", "HEAD"
-
status, headers, body = @app.call(env)
-
headers = Utils::HeaderHash.new(headers)
-
if status == 200 && fresh?(env, headers)
-
status = 304
-
headers.delete('Content-Type')
-
headers.delete('Content-Length')
-
body = []
-
end
-
[status, headers, body]
-
else
-
@app.call(env)
-
end
-
end
-
-
1
private
-
-
1
def fresh?(env, headers)
-
modified_since = env['HTTP_IF_MODIFIED_SINCE']
-
none_match = env['HTTP_IF_NONE_MATCH']
-
-
return false unless modified_since || none_match
-
-
success = true
-
success &&= modified_since?(to_rfc2822(modified_since), headers) if modified_since
-
success &&= etag_matches?(none_match, headers) if none_match
-
success
-
end
-
-
1
def etag_matches?(none_match, headers)
-
etag = headers['ETag'] and etag == none_match
-
end
-
-
1
def modified_since?(modified_since, headers)
-
last_modified = to_rfc2822(headers['Last-Modified']) and
-
modified_since and
-
modified_since >= last_modified
-
end
-
-
1
def to_rfc2822(since)
-
Time.rfc2822(since) rescue nil
-
end
-
end
-
end
-
1
require 'digest/md5'
-
-
1
module Rack
-
# Automatically sets the ETag header on all String bodies.
-
#
-
# The ETag header is skipped if ETag or Last-Modified headers are sent or if
-
# a sendfile body (body.responds_to :to_path) is given (since such cases
-
# should be handled by apache/nginx).
-
#
-
# On initialization, you can pass two parameters: a Cache-Control directive
-
# used when Etag is absent and a directive when it is present. The first
-
# defaults to nil, while the second defaults to "max-age=0, private, must-revalidate"
-
1
class ETag
-
1
DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate".freeze
-
-
1
def initialize(app, no_cache_control = nil, cache_control = DEFAULT_CACHE_CONTROL)
-
1
@app = app
-
1
@cache_control = cache_control
-
1
@no_cache_control = no_cache_control
-
end
-
-
1
def call(env)
-
status, headers, body = @app.call(env)
-
-
if etag_status?(status) && etag_body?(body) && !skip_caching?(headers)
-
digest, body = digest_body(body)
-
headers['ETag'] = %("#{digest}") if digest
-
end
-
-
unless headers['Cache-Control']
-
headers['Cache-Control'] =
-
(digest ? @cache_control : @no_cache_control) || []
-
end
-
-
[status, headers, body]
-
end
-
-
1
private
-
-
1
def etag_status?(status)
-
status == 200 || status == 201
-
end
-
-
1
def etag_body?(body)
-
!body.respond_to?(:to_path)
-
end
-
-
1
def skip_caching?(headers)
-
headers['Cache-Control'] == 'no-cache' ||
-
headers.key?('ETag') || headers.key?('Last-Modified')
-
end
-
-
1
def digest_body(body)
-
parts = []
-
body.each { |part| parts << part }
-
string_body = parts.join
-
digest = Digest::MD5.hexdigest(string_body) unless string_body.empty?
-
[digest, parts]
-
end
-
end
-
end
-
1
require 'time'
-
1
require 'rack/utils'
-
1
require 'rack/mime'
-
-
1
module Rack
-
# Rack::File serves files below the +root+ directory given, according to the
-
# path info of the Rack request.
-
# e.g. when Rack::File.new("/etc") is used, you can access 'passwd' file
-
# as http://localhost:9292/passwd
-
#
-
# Handlers can detect if bodies are a Rack::File, and use mechanisms
-
# like sendfile on the +path+.
-
-
1
class File
-
1
SEPS = Regexp.union(*[::File::SEPARATOR, ::File::ALT_SEPARATOR].compact)
-
-
1
attr_accessor :root
-
1
attr_accessor :path
-
1
attr_accessor :cache_control
-
-
1
alias :to_path :path
-
-
1
def initialize(root, cache_control = nil)
-
1
@root = root
-
1
@cache_control = cache_control
-
end
-
-
1
def call(env)
-
dup._call(env)
-
end
-
-
1
F = ::File
-
-
1
def _call(env)
-
@path_info = Utils.unescape(env["PATH_INFO"])
-
parts = @path_info.split SEPS
-
-
return fail(403, "Forbidden") if parts.include? ".."
-
-
@path = F.join(@root, *parts)
-
-
available = begin
-
F.file?(@path) && F.readable?(@path)
-
rescue SystemCallError
-
false
-
end
-
-
if available
-
serving(env)
-
else
-
fail(404, "File not found: #{@path_info}")
-
end
-
end
-
-
1
def serving(env)
-
# NOTE:
-
# We check via File::size? whether this file provides size info
-
# via stat (e.g. /proc files often don't), otherwise we have to
-
# figure it out by reading the whole file into memory.
-
size = F.size?(@path) || Utils.bytesize(F.read(@path))
-
-
response = [
-
200,
-
{
-
"Last-Modified" => F.mtime(@path).httpdate,
-
"Content-Type" => Mime.mime_type(F.extname(@path), 'text/plain')
-
},
-
self
-
]
-
response[1].merge! 'Cache-Control' => @cache_control if @cache_control
-
-
ranges = Rack::Utils.byte_ranges(env, size)
-
if ranges.nil? || ranges.length > 1
-
# No ranges, or multiple ranges (which we don't support):
-
# TODO: Support multiple byte-ranges
-
response[0] = 200
-
@range = 0..size-1
-
elsif ranges.empty?
-
# Unsatisfiable. Return error, and file size:
-
response = fail(416, "Byte range unsatisfiable")
-
response[1]["Content-Range"] = "bytes */#{size}"
-
return response
-
else
-
# Partial content:
-
@range = ranges[0]
-
response[0] = 206
-
response[1]["Content-Range"] = "bytes #{@range.begin}-#{@range.end}/#{size}"
-
size = @range.end - @range.begin + 1
-
end
-
-
response[1]["Content-Length"] = size.to_s
-
response
-
end
-
-
1
def each
-
F.open(@path, "rb") do |file|
-
file.seek(@range.begin)
-
remaining_len = @range.end-@range.begin+1
-
while remaining_len > 0
-
part = file.read([8192, remaining_len].min)
-
break unless part
-
remaining_len -= part.length
-
-
yield part
-
end
-
end
-
end
-
-
1
private
-
-
1
def fail(status, body)
-
body += "\n"
-
[
-
status,
-
{
-
"Content-Type" => "text/plain",
-
"Content-Length" => body.size.to_s,
-
"X-Cascade" => "pass"
-
},
-
[body]
-
]
-
end
-
-
end
-
end
-
1
require 'rack/utils'
-
-
1
module Rack
-
# Rack::Lint validates your application and the requests and
-
# responses according to the Rack spec.
-
-
1
class Lint
-
1
def initialize(app)
-
@app = app
-
@content_length = nil
-
end
-
-
# :stopdoc:
-
-
1
class LintError < RuntimeError; end
-
1
module Assertion
-
1
def assert(message, &block)
-
unless block.call
-
raise LintError, message
-
end
-
end
-
end
-
1
include Assertion
-
-
## This specification aims to formalize the Rack protocol. You
-
## can (and should) use Rack::Lint to enforce it.
-
##
-
## When you develop middleware, be sure to add a Lint before and
-
## after to catch all mistakes.
-
-
## = Rack applications
-
-
## A Rack application is a Ruby object (not a class) that
-
## responds to +call+.
-
1
def call(env=nil)
-
dup._call(env)
-
end
-
-
1
def _call(env)
-
## It takes exactly one argument, the *environment*
-
assert("No env given") { env }
-
check_env env
-
-
env['rack.input'] = InputWrapper.new(env['rack.input'])
-
env['rack.errors'] = ErrorWrapper.new(env['rack.errors'])
-
-
## and returns an Array of exactly three values:
-
status, headers, @body = @app.call(env)
-
## The *status*,
-
check_status status
-
## the *headers*,
-
check_headers headers
-
## and the *body*.
-
check_content_type status, headers
-
check_content_length status, headers
-
@head_request = env["REQUEST_METHOD"] == "HEAD"
-
[status, headers, self]
-
end
-
-
## == The Environment
-
1
def check_env(env)
-
## The environment must be an instance of Hash that includes
-
## CGI-like headers. The application is free to modify the
-
## environment.
-
assert("env #{env.inspect} is not a Hash, but #{env.class}") {
-
env.kind_of? Hash
-
}
-
-
##
-
## The environment is required to include these variables
-
## (adopted from PEP333), except when they'd be empty, but see
-
## below.
-
-
## <tt>REQUEST_METHOD</tt>:: The HTTP request method, such as
-
## "GET" or "POST". This cannot ever
-
## be an empty string, and so is
-
## always required.
-
-
## <tt>SCRIPT_NAME</tt>:: The initial portion of the request
-
## URL's "path" that corresponds to the
-
## application object, so that the
-
## application knows its virtual
-
## "location". This may be an empty
-
## string, if the application corresponds
-
## to the "root" of the server.
-
-
## <tt>PATH_INFO</tt>:: The remainder of the request URL's
-
## "path", designating the virtual
-
## "location" of the request's target
-
## within the application. This may be an
-
## empty string, if the request URL targets
-
## the application root and does not have a
-
## trailing slash. This value may be
-
## percent-encoded when I originating from
-
## a URL.
-
-
## <tt>QUERY_STRING</tt>:: The portion of the request URL that
-
## follows the <tt>?</tt>, if any. May be
-
## empty, but is always required!
-
-
## <tt>SERVER_NAME</tt>, <tt>SERVER_PORT</tt>:: When combined with <tt>SCRIPT_NAME</tt> and <tt>PATH_INFO</tt>, these variables can be used to complete the URL. Note, however, that <tt>HTTP_HOST</tt>, if present, should be used in preference to <tt>SERVER_NAME</tt> for reconstructing the request URL. <tt>SERVER_NAME</tt> and <tt>SERVER_PORT</tt> can never be empty strings, and so are always required.
-
-
## <tt>HTTP_</tt> Variables:: Variables corresponding to the
-
## client-supplied HTTP request
-
## headers (i.e., variables whose
-
## names begin with <tt>HTTP_</tt>). The
-
## presence or absence of these
-
## variables should correspond with
-
## the presence or absence of the
-
## appropriate HTTP header in the
-
## request.
-
-
## In addition to this, the Rack environment must include these
-
## Rack-specific variables:
-
-
## <tt>rack.version</tt>:: The Array [1,1], representing this version of Rack.
-
## <tt>rack.url_scheme</tt>:: +http+ or +https+, depending on the request URL.
-
## <tt>rack.input</tt>:: See below, the input stream.
-
## <tt>rack.errors</tt>:: See below, the error stream.
-
## <tt>rack.multithread</tt>:: true if the application object may be simultaneously invoked by another thread in the same process, false otherwise.
-
## <tt>rack.multiprocess</tt>:: true if an equivalent application object may be simultaneously invoked by another process, false otherwise.
-
## <tt>rack.run_once</tt>:: true if the server expects (but does not guarantee!) that the application will only be invoked this one time during the life of its containing process. Normally, this will only be true for a server based on CGI (or something similar).
-
##
-
-
## Additional environment specifications have approved to
-
## standardized middleware APIs. None of these are required to
-
## be implemented by the server.
-
-
## <tt>rack.session</tt>:: A hash like interface for storing request session data.
-
## The store must implement:
-
if session = env['rack.session']
-
## store(key, value) (aliased as []=);
-
assert("session #{session.inspect} must respond to store and []=") {
-
session.respond_to?(:store) && session.respond_to?(:[]=)
-
}
-
-
## fetch(key, default = nil) (aliased as []);
-
assert("session #{session.inspect} must respond to fetch and []") {
-
session.respond_to?(:fetch) && session.respond_to?(:[])
-
}
-
-
## delete(key);
-
assert("session #{session.inspect} must respond to delete") {
-
session.respond_to?(:delete)
-
}
-
-
## clear;
-
assert("session #{session.inspect} must respond to clear") {
-
session.respond_to?(:clear)
-
}
-
end
-
-
## <tt>rack.logger</tt>:: A common object interface for logging messages.
-
## The object must implement:
-
if logger = env['rack.logger']
-
## info(message, &block)
-
assert("logger #{logger.inspect} must respond to info") {
-
logger.respond_to?(:info)
-
}
-
-
## debug(message, &block)
-
assert("logger #{logger.inspect} must respond to debug") {
-
logger.respond_to?(:debug)
-
}
-
-
## warn(message, &block)
-
assert("logger #{logger.inspect} must respond to warn") {
-
logger.respond_to?(:warn)
-
}
-
-
## error(message, &block)
-
assert("logger #{logger.inspect} must respond to error") {
-
logger.respond_to?(:error)
-
}
-
-
## fatal(message, &block)
-
assert("logger #{logger.inspect} must respond to fatal") {
-
logger.respond_to?(:fatal)
-
}
-
end
-
-
## The server or the application can store their own data in the
-
## environment, too. The keys must contain at least one dot,
-
## and should be prefixed uniquely. The prefix <tt>rack.</tt>
-
## is reserved for use with the Rack core distribution and other
-
## accepted specifications and must not be used otherwise.
-
##
-
-
%w[REQUEST_METHOD SERVER_NAME SERVER_PORT
-
QUERY_STRING
-
rack.version rack.input rack.errors
-
rack.multithread rack.multiprocess rack.run_once].each { |header|
-
assert("env missing required key #{header}") { env.include? header }
-
}
-
-
## The environment must not contain the keys
-
## <tt>HTTP_CONTENT_TYPE</tt> or <tt>HTTP_CONTENT_LENGTH</tt>
-
## (use the versions without <tt>HTTP_</tt>).
-
%w[HTTP_CONTENT_TYPE HTTP_CONTENT_LENGTH].each { |header|
-
assert("env contains #{header}, must use #{header[5,-1]}") {
-
not env.include? header
-
}
-
}
-
-
## The CGI keys (named without a period) must have String values.
-
env.each { |key, value|
-
next if key.include? "." # Skip extensions
-
assert("env variable #{key} has non-string value #{value.inspect}") {
-
value.kind_of? String
-
}
-
}
-
-
##
-
## There are the following restrictions:
-
-
## * <tt>rack.version</tt> must be an array of Integers.
-
assert("rack.version must be an Array, was #{env["rack.version"].class}") {
-
env["rack.version"].kind_of? Array
-
}
-
## * <tt>rack.url_scheme</tt> must either be +http+ or +https+.
-
assert("rack.url_scheme unknown: #{env["rack.url_scheme"].inspect}") {
-
%w[http https].include? env["rack.url_scheme"]
-
}
-
-
## * There must be a valid input stream in <tt>rack.input</tt>.
-
check_input env["rack.input"]
-
## * There must be a valid error stream in <tt>rack.errors</tt>.
-
check_error env["rack.errors"]
-
-
## * The <tt>REQUEST_METHOD</tt> must be a valid token.
-
assert("REQUEST_METHOD unknown: #{env["REQUEST_METHOD"]}") {
-
env["REQUEST_METHOD"] =~ /\A[0-9A-Za-z!\#$%&'*+.^_`|~-]+\z/
-
}
-
-
## * The <tt>SCRIPT_NAME</tt>, if non-empty, must start with <tt>/</tt>
-
assert("SCRIPT_NAME must start with /") {
-
!env.include?("SCRIPT_NAME") ||
-
env["SCRIPT_NAME"] == "" ||
-
env["SCRIPT_NAME"] =~ /\A\//
-
}
-
## * The <tt>PATH_INFO</tt>, if non-empty, must start with <tt>/</tt>
-
assert("PATH_INFO must start with /") {
-
!env.include?("PATH_INFO") ||
-
env["PATH_INFO"] == "" ||
-
env["PATH_INFO"] =~ /\A\//
-
}
-
## * The <tt>CONTENT_LENGTH</tt>, if given, must consist of digits only.
-
assert("Invalid CONTENT_LENGTH: #{env["CONTENT_LENGTH"]}") {
-
!env.include?("CONTENT_LENGTH") || env["CONTENT_LENGTH"] =~ /\A\d+\z/
-
}
-
-
## * One of <tt>SCRIPT_NAME</tt> or <tt>PATH_INFO</tt> must be
-
## set. <tt>PATH_INFO</tt> should be <tt>/</tt> if
-
## <tt>SCRIPT_NAME</tt> is empty.
-
assert("One of SCRIPT_NAME or PATH_INFO must be set (make PATH_INFO '/' if SCRIPT_NAME is empty)") {
-
env["SCRIPT_NAME"] || env["PATH_INFO"]
-
}
-
## <tt>SCRIPT_NAME</tt> never should be <tt>/</tt>, but instead be empty.
-
assert("SCRIPT_NAME cannot be '/', make it '' and PATH_INFO '/'") {
-
env["SCRIPT_NAME"] != "/"
-
}
-
end
-
-
## === The Input Stream
-
##
-
## The input stream is an IO-like object which contains the raw HTTP
-
## POST data.
-
1
def check_input(input)
-
## When applicable, its external encoding must be "ASCII-8BIT" and it
-
## must be opened in binary mode, for Ruby 1.9 compatibility.
-
assert("rack.input #{input} does not have ASCII-8BIT as its external encoding") {
-
input.external_encoding.name == "ASCII-8BIT"
-
} if input.respond_to?(:external_encoding)
-
assert("rack.input #{input} is not opened in binary mode") {
-
input.binmode?
-
} if input.respond_to?(:binmode?)
-
-
## The input stream must respond to +gets+, +each+, +read+ and +rewind+.
-
[:gets, :each, :read, :rewind].each { |method|
-
assert("rack.input #{input} does not respond to ##{method}") {
-
input.respond_to? method
-
}
-
}
-
end
-
-
1
class InputWrapper
-
1
include Assertion
-
-
1
def initialize(input)
-
@input = input
-
end
-
-
## * +gets+ must be called without arguments and return a string,
-
## or +nil+ on EOF.
-
1
def gets(*args)
-
assert("rack.input#gets called with arguments") { args.size == 0 }
-
v = @input.gets
-
assert("rack.input#gets didn't return a String") {
-
v.nil? or v.kind_of? String
-
}
-
v
-
end
-
-
## * +read+ behaves like IO#read. Its signature is <tt>read([length, [buffer]])</tt>.
-
## If given, +length+ must be a non-negative Integer (>= 0) or +nil+, and +buffer+ must
-
## be a String and may not be nil. If +length+ is given and not nil, then this method
-
## reads at most +length+ bytes from the input stream. If +length+ is not given or nil,
-
## then this method reads all data until EOF.
-
## When EOF is reached, this method returns nil if +length+ is given and not nil, or ""
-
## if +length+ is not given or is nil.
-
## If +buffer+ is given, then the read data will be placed into +buffer+ instead of a
-
## newly created String object.
-
1
def read(*args)
-
assert("rack.input#read called with too many arguments") {
-
args.size <= 2
-
}
-
if args.size >= 1
-
assert("rack.input#read called with non-integer and non-nil length") {
-
args.first.kind_of?(Integer) || args.first.nil?
-
}
-
assert("rack.input#read called with a negative length") {
-
args.first.nil? || args.first >= 0
-
}
-
end
-
if args.size >= 2
-
assert("rack.input#read called with non-String buffer") {
-
args[1].kind_of?(String)
-
}
-
end
-
-
v = @input.read(*args)
-
-
assert("rack.input#read didn't return nil or a String") {
-
v.nil? or v.kind_of? String
-
}
-
if args[0].nil?
-
assert("rack.input#read(nil) returned nil on EOF") {
-
!v.nil?
-
}
-
end
-
-
v
-
end
-
-
## * +each+ must be called without arguments and only yield Strings.
-
1
def each(*args)
-
assert("rack.input#each called with arguments") { args.size == 0 }
-
@input.each { |line|
-
assert("rack.input#each didn't yield a String") {
-
line.kind_of? String
-
}
-
yield line
-
}
-
end
-
-
## * +rewind+ must be called without arguments. It rewinds the input
-
## stream back to the beginning. It must not raise Errno::ESPIPE:
-
## that is, it may not be a pipe or a socket. Therefore, handler
-
## developers must buffer the input data into some rewindable object
-
## if the underlying input stream is not rewindable.
-
1
def rewind(*args)
-
assert("rack.input#rewind called with arguments") { args.size == 0 }
-
assert("rack.input#rewind raised Errno::ESPIPE") {
-
begin
-
@input.rewind
-
true
-
rescue Errno::ESPIPE
-
false
-
end
-
}
-
end
-
-
## * +close+ must never be called on the input stream.
-
1
def close(*args)
-
assert("rack.input#close must not be called") { false }
-
end
-
end
-
-
## === The Error Stream
-
1
def check_error(error)
-
## The error stream must respond to +puts+, +write+ and +flush+.
-
[:puts, :write, :flush].each { |method|
-
assert("rack.error #{error} does not respond to ##{method}") {
-
error.respond_to? method
-
}
-
}
-
end
-
-
1
class ErrorWrapper
-
1
include Assertion
-
-
1
def initialize(error)
-
@error = error
-
end
-
-
## * +puts+ must be called with a single argument that responds to +to_s+.
-
1
def puts(str)
-
@error.puts str
-
end
-
-
## * +write+ must be called with a single argument that is a String.
-
1
def write(str)
-
assert("rack.errors#write not called with a String") { str.kind_of? String }
-
@error.write str
-
end
-
-
## * +flush+ must be called without arguments and must be called
-
## in order to make the error appear for sure.
-
1
def flush
-
@error.flush
-
end
-
-
## * +close+ must never be called on the error stream.
-
1
def close(*args)
-
assert("rack.errors#close must not be called") { false }
-
end
-
end
-
-
## == The Response
-
-
## === The Status
-
1
def check_status(status)
-
## This is an HTTP status. When parsed as integer (+to_i+), it must be
-
## greater than or equal to 100.
-
assert("Status must be >=100 seen as integer") { status.to_i >= 100 }
-
end
-
-
## === The Headers
-
1
def check_headers(header)
-
## The header must respond to +each+, and yield values of key and value.
-
assert("headers object should respond to #each, but doesn't (got #{header.class} as headers)") {
-
header.respond_to? :each
-
}
-
header.each { |key, value|
-
## The header keys must be Strings.
-
assert("header key must be a string, was #{key.class}") {
-
key.kind_of? String
-
}
-
## The header must not contain a +Status+ key,
-
assert("header must not contain Status") { key.downcase != "status" }
-
## contain keys with <tt>:</tt> or newlines in their name,
-
assert("header names must not contain : or \\n") { key !~ /[:\n]/ }
-
## contain keys names that end in <tt>-</tt> or <tt>_</tt>,
-
assert("header names must not end in - or _") { key !~ /[-_]\z/ }
-
## but only contain keys that consist of
-
## letters, digits, <tt>_</tt> or <tt>-</tt> and start with a letter.
-
assert("invalid header name: #{key}") { key =~ /\A[a-zA-Z][a-zA-Z0-9_-]*\z/ }
-
-
## The values of the header must be Strings,
-
assert("a header value must be a String, but the value of " +
-
"'#{key}' is a #{value.class}") { value.kind_of? String }
-
## consisting of lines (for multiple header values, e.g. multiple
-
## <tt>Set-Cookie</tt> values) seperated by "\n".
-
value.split("\n").each { |item|
-
## The lines must not contain characters below 037.
-
assert("invalid header value #{key}: #{item.inspect}") {
-
item !~ /[\000-\037]/
-
}
-
}
-
}
-
end
-
-
## === The Content-Type
-
1
def check_content_type(status, headers)
-
headers.each { |key, value|
-
## There must be a <tt>Content-Type</tt>, except when the
-
## +Status+ is 1xx, 204 or 304, in which case there must be none
-
## given.
-
if key.downcase == "content-type"
-
assert("Content-Type header found in #{status} response, not allowed") {
-
not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
-
}
-
return
-
end
-
}
-
assert("No Content-Type header found") {
-
Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
-
}
-
end
-
-
## === The Content-Length
-
1
def check_content_length(status, headers)
-
headers.each { |key, value|
-
if key.downcase == 'content-length'
-
## There must not be a <tt>Content-Length</tt> header when the
-
## +Status+ is 1xx, 204 or 304.
-
assert("Content-Length header found in #{status} response, not allowed") {
-
not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
-
}
-
@content_length = value
-
end
-
}
-
end
-
-
1
def verify_content_length(bytes)
-
if @head_request
-
assert("Response body was given for HEAD request, but should be empty") {
-
bytes == 0
-
}
-
elsif @content_length
-
assert("Content-Length header was #{@content_length}, but should be #{bytes}") {
-
@content_length == bytes.to_s
-
}
-
end
-
end
-
-
## === The Body
-
1
def each
-
@closed = false
-
bytes = 0
-
-
## The Body must respond to +each+
-
assert("Response body must respond to each") do
-
@body.respond_to?(:each)
-
end
-
-
@body.each { |part|
-
## and must only yield String values.
-
assert("Body yielded non-string value #{part.inspect}") {
-
part.kind_of? String
-
}
-
bytes += Rack::Utils.bytesize(part)
-
yield part
-
}
-
verify_content_length(bytes)
-
-
##
-
## The Body itself should not be an instance of String, as this will
-
## break in Ruby 1.9.
-
##
-
## If the Body responds to +close+, it will be called after iteration.
-
# XXX howto: assert("Body has not been closed") { @closed }
-
-
-
##
-
## If the Body responds to +to_path+, it must return a String
-
## identifying the location of a file whose contents are identical
-
## to that produced by calling +each+; this may be used by the
-
## server as an alternative, possibly more efficient way to
-
## transport the response.
-
-
if @body.respond_to?(:to_path)
-
assert("The file identified by body.to_path does not exist") {
-
::File.exist? @body.to_path
-
}
-
end
-
-
##
-
## The Body commonly is an Array of Strings, the application
-
## instance itself, or a File-like object.
-
end
-
-
1
def close
-
@closed = true
-
@body.close if @body.respond_to?(:close)
-
end
-
-
# :startdoc:
-
-
end
-
end
-
-
## == Thanks
-
## Some parts of this specification are adopted from PEP333: Python
-
## Web Server Gateway Interface
-
## v1.0 (http://www.python.org/dev/peps/pep-0333/). I'd like to thank
-
## everyone involved in that effort.
-
1
require 'thread'
-
1
require 'rack/body_proxy'
-
-
1
module Rack
-
1
class Lock
-
1
FLAG = 'rack.multithread'.freeze
-
-
1
def initialize(app, mutex = Mutex.new)
-
1
@app, @mutex = app, mutex
-
end
-
-
1
def call(env)
-
old, env[FLAG] = env[FLAG], false
-
@mutex.lock
-
response = @app.call(env)
-
response[2] = BodyProxy.new(response[2]) { @mutex.unlock }
-
response
-
rescue Exception
-
@mutex.unlock
-
raise
-
ensure
-
env[FLAG] = old
-
end
-
end
-
end
-
1
module Rack
-
1
class MethodOverride
-
1
HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS PATCH)
-
-
1
METHOD_OVERRIDE_PARAM_KEY = "_method".freeze
-
1
HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE".freeze
-
-
1
def initialize(app)
-
1
@app = app
-
end
-
-
1
def call(env)
-
if env["REQUEST_METHOD"] == "POST"
-
req = Request.new(env)
-
method = req.POST[METHOD_OVERRIDE_PARAM_KEY] ||
-
env[HTTP_METHOD_OVERRIDE_HEADER]
-
method = method.to_s.upcase
-
if HTTP_METHODS.include?(method)
-
env["rack.methodoverride.original_method"] = env["REQUEST_METHOD"]
-
env["REQUEST_METHOD"] = method
-
end
-
end
-
-
@app.call(env)
-
end
-
end
-
end
-
1
module Rack
-
1
module Mime
-
# Returns String with mime type if found, otherwise use +fallback+.
-
# +ext+ should be filename extension in the '.ext' format that
-
# File.extname(file) returns.
-
# +fallback+ may be any object
-
#
-
# Also see the documentation for MIME_TYPES
-
#
-
# Usage:
-
# Rack::Mime.mime_type('.foo')
-
#
-
# This is a shortcut for:
-
# Rack::Mime::MIME_TYPES.fetch('.foo', 'application/octet-stream')
-
-
1
def mime_type(ext, fallback='application/octet-stream')
-
MIME_TYPES.fetch(ext.to_s.downcase, fallback)
-
end
-
1
module_function :mime_type
-
-
# List of most common mime-types, selected various sources
-
# according to their usefulness in a webserving scope for Ruby
-
# users.
-
#
-
# To amend this list with your local mime.types list you can use:
-
#
-
# require 'webrick/httputils'
-
# list = WEBrick::HTTPUtils.load_mime_types('/etc/mime.types')
-
# Rack::Mime::MIME_TYPES.merge!(list)
-
#
-
# N.B. On Ubuntu the mime.types file does not include the leading period, so
-
# users may need to modify the data before merging into the hash.
-
#
-
# To add the list mongrel provides, use:
-
#
-
# require 'mongrel/handlers'
-
# Rack::Mime::MIME_TYPES.merge!(Mongrel::DirHandler::MIME_TYPES)
-
-
1
MIME_TYPES = {
-
".3gp" => "video/3gpp",
-
".a" => "application/octet-stream",
-
".ai" => "application/postscript",
-
".aif" => "audio/x-aiff",
-
".aiff" => "audio/x-aiff",
-
".asc" => "application/pgp-signature",
-
".asf" => "video/x-ms-asf",
-
".asm" => "text/x-asm",
-
".asx" => "video/x-ms-asf",
-
".atom" => "application/atom+xml",
-
".au" => "audio/basic",
-
".avi" => "video/x-msvideo",
-
".bat" => "application/x-msdownload",
-
".bin" => "application/octet-stream",
-
".bmp" => "image/bmp",
-
".bz2" => "application/x-bzip2",
-
".c" => "text/x-c",
-
".cab" => "application/vnd.ms-cab-compressed",
-
".cc" => "text/x-c",
-
".chm" => "application/vnd.ms-htmlhelp",
-
".class" => "application/octet-stream",
-
".com" => "application/x-msdownload",
-
".conf" => "text/plain",
-
".cpp" => "text/x-c",
-
".crt" => "application/x-x509-ca-cert",
-
".css" => "text/css",
-
".csv" => "text/csv",
-
".cxx" => "text/x-c",
-
".deb" => "application/x-debian-package",
-
".der" => "application/x-x509-ca-cert",
-
".diff" => "text/x-diff",
-
".djv" => "image/vnd.djvu",
-
".djvu" => "image/vnd.djvu",
-
".dll" => "application/x-msdownload",
-
".dmg" => "application/octet-stream",
-
".doc" => "application/msword",
-
".dot" => "application/msword",
-
".dtd" => "application/xml-dtd",
-
".dvi" => "application/x-dvi",
-
".ear" => "application/java-archive",
-
".eml" => "message/rfc822",
-
".eps" => "application/postscript",
-
".exe" => "application/x-msdownload",
-
".f" => "text/x-fortran",
-
".f77" => "text/x-fortran",
-
".f90" => "text/x-fortran",
-
".flv" => "video/x-flv",
-
".for" => "text/x-fortran",
-
".gem" => "application/octet-stream",
-
".gemspec" => "text/x-script.ruby",
-
".gif" => "image/gif",
-
".gz" => "application/x-gzip",
-
".h" => "text/x-c",
-
".htc" => "text/x-component",
-
".hh" => "text/x-c",
-
".htm" => "text/html",
-
".html" => "text/html",
-
".ico" => "image/vnd.microsoft.icon",
-
".ics" => "text/calendar",
-
".ifb" => "text/calendar",
-
".iso" => "application/octet-stream",
-
".jar" => "application/java-archive",
-
".java" => "text/x-java-source",
-
".jnlp" => "application/x-java-jnlp-file",
-
".jpeg" => "image/jpeg",
-
".jpg" => "image/jpeg",
-
".js" => "application/javascript",
-
".json" => "application/json",
-
".log" => "text/plain",
-
".m3u" => "audio/x-mpegurl",
-
".m4v" => "video/mp4",
-
".man" => "text/troff",
-
".manifest"=> "text/cache-manifest",
-
".mathml" => "application/mathml+xml",
-
".mbox" => "application/mbox",
-
".mdoc" => "text/troff",
-
".me" => "text/troff",
-
".mid" => "audio/midi",
-
".midi" => "audio/midi",
-
".mime" => "message/rfc822",
-
".mml" => "application/mathml+xml",
-
".mng" => "video/x-mng",
-
".mov" => "video/quicktime",
-
".mp3" => "audio/mpeg",
-
".mp4" => "video/mp4",
-
".mp4v" => "video/mp4",
-
".mpeg" => "video/mpeg",
-
".mpg" => "video/mpeg",
-
".ms" => "text/troff",
-
".msi" => "application/x-msdownload",
-
".odp" => "application/vnd.oasis.opendocument.presentation",
-
".ods" => "application/vnd.oasis.opendocument.spreadsheet",
-
".odt" => "application/vnd.oasis.opendocument.text",
-
".ogg" => "application/ogg",
-
".ogv" => "video/ogg",
-
".p" => "text/x-pascal",
-
".pas" => "text/x-pascal",
-
".pbm" => "image/x-portable-bitmap",
-
".pdf" => "application/pdf",
-
".pem" => "application/x-x509-ca-cert",
-
".pgm" => "image/x-portable-graymap",
-
".pgp" => "application/pgp-encrypted",
-
".pkg" => "application/octet-stream",
-
".pl" => "text/x-script.perl",
-
".pm" => "text/x-script.perl-module",
-
".png" => "image/png",
-
".pnm" => "image/x-portable-anymap",
-
".ppm" => "image/x-portable-pixmap",
-
".pps" => "application/vnd.ms-powerpoint",
-
".ppt" => "application/vnd.ms-powerpoint",
-
".ps" => "application/postscript",
-
".psd" => "image/vnd.adobe.photoshop",
-
".py" => "text/x-script.python",
-
".qt" => "video/quicktime",
-
".ra" => "audio/x-pn-realaudio",
-
".rake" => "text/x-script.ruby",
-
".ram" => "audio/x-pn-realaudio",
-
".rar" => "application/x-rar-compressed",
-
".rb" => "text/x-script.ruby",
-
".rdf" => "application/rdf+xml",
-
".roff" => "text/troff",
-
".rpm" => "application/x-redhat-package-manager",
-
".rss" => "application/rss+xml",
-
".rtf" => "application/rtf",
-
".ru" => "text/x-script.ruby",
-
".s" => "text/x-asm",
-
".sgm" => "text/sgml",
-
".sgml" => "text/sgml",
-
".sh" => "application/x-sh",
-
".sig" => "application/pgp-signature",
-
".snd" => "audio/basic",
-
".so" => "application/octet-stream",
-
".svg" => "image/svg+xml",
-
".svgz" => "image/svg+xml",
-
".swf" => "application/x-shockwave-flash",
-
".t" => "text/troff",
-
".tar" => "application/x-tar",
-
".tbz" => "application/x-bzip-compressed-tar",
-
".tcl" => "application/x-tcl",
-
".tex" => "application/x-tex",
-
".texi" => "application/x-texinfo",
-
".texinfo" => "application/x-texinfo",
-
".text" => "text/plain",
-
".tif" => "image/tiff",
-
".tiff" => "image/tiff",
-
".torrent" => "application/x-bittorrent",
-
".tr" => "text/troff",
-
".ttf" => "application/octet-stream",
-
".txt" => "text/plain",
-
".vcf" => "text/x-vcard",
-
".vcs" => "text/x-vcalendar",
-
".vrml" => "model/vrml",
-
".war" => "application/java-archive",
-
".wav" => "audio/x-wav",
-
".webm" => "video/webm",
-
".wma" => "audio/x-ms-wma",
-
".wmv" => "video/x-ms-wmv",
-
".wmx" => "video/x-ms-wmx",
-
".woff" => "application/octet-stream",
-
".wrl" => "model/vrml",
-
".wsdl" => "application/wsdl+xml",
-
".xbm" => "image/x-xbitmap",
-
".xhtml" => "application/xhtml+xml",
-
".xls" => "application/vnd.ms-excel",
-
".xml" => "application/xml",
-
".xpm" => "image/x-xpixmap",
-
".xsl" => "application/xml",
-
".xslt" => "application/xslt+xml",
-
".yaml" => "text/yaml",
-
".yml" => "text/yaml",
-
".zip" => "application/zip",
-
}
-
end
-
end
-
1
require 'uri'
-
1
require 'stringio'
-
1
require 'rack'
-
1
require 'rack/lint'
-
1
require 'rack/utils'
-
1
require 'rack/response'
-
-
1
module Rack
-
# Rack::MockRequest helps testing your Rack application without
-
# actually using HTTP.
-
#
-
# After performing a request on a URL with get/post/put/delete, it
-
# returns a MockResponse with useful helper methods for effective
-
# testing.
-
#
-
# You can pass a hash with additional configuration to the
-
# get/post/put/delete.
-
# <tt>:input</tt>:: A String or IO-like to be used as rack.input.
-
# <tt>:fatal</tt>:: Raise a FatalWarning if the app writes to rack.errors.
-
# <tt>:lint</tt>:: If true, wrap the application in a Rack::Lint.
-
-
1
class MockRequest
-
1
class FatalWarning < RuntimeError
-
end
-
-
1
class FatalWarner
-
1
def puts(warning)
-
raise FatalWarning, warning
-
end
-
-
1
def write(warning)
-
raise FatalWarning, warning
-
end
-
-
1
def flush
-
end
-
-
1
def string
-
""
-
end
-
end
-
-
1
DEFAULT_ENV = {
-
"rack.version" => Rack::VERSION,
-
"rack.input" => StringIO.new,
-
"rack.errors" => StringIO.new,
-
"rack.multithread" => true,
-
"rack.multiprocess" => true,
-
"rack.run_once" => false,
-
}
-
-
1
def initialize(app)
-
@app = app
-
end
-
-
1
def get(uri, opts={}) request("GET", uri, opts) end
-
1
def post(uri, opts={}) request("POST", uri, opts) end
-
1
def put(uri, opts={}) request("PUT", uri, opts) end
-
1
def delete(uri, opts={}) request("DELETE", uri, opts) end
-
-
1
def request(method="GET", uri="", opts={})
-
env = self.class.env_for(uri, opts.merge(:method => method))
-
-
if opts[:lint]
-
app = Rack::Lint.new(@app)
-
else
-
app = @app
-
end
-
-
errors = env["rack.errors"]
-
status, headers, body = app.call(env)
-
MockResponse.new(status, headers, body, errors)
-
ensure
-
body.close if body.respond_to?(:close)
-
end
-
-
# Return the Rack environment used for a request to +uri+.
-
1
def self.env_for(uri="", opts={})
-
1
uri = URI(uri)
-
1
uri.path = "/#{uri.path}" unless uri.path[0] == ?/
-
-
1
env = DEFAULT_ENV.dup
-
-
1
env["REQUEST_METHOD"] = opts[:method] ? opts[:method].to_s.upcase : "GET"
-
1
env["SERVER_NAME"] = uri.host || "example.org"
-
1
env["SERVER_PORT"] = uri.port ? uri.port.to_s : "80"
-
1
env["QUERY_STRING"] = uri.query.to_s
-
1
env["PATH_INFO"] = (!uri.path || uri.path.empty?) ? "/" : uri.path
-
1
env["rack.url_scheme"] = uri.scheme || "http"
-
1
env["HTTPS"] = env["rack.url_scheme"] == "https" ? "on" : "off"
-
-
1
env["SCRIPT_NAME"] = opts[:script_name] || ""
-
-
1
if opts[:fatal]
-
env["rack.errors"] = FatalWarner.new
-
else
-
1
env["rack.errors"] = StringIO.new
-
end
-
-
1
if params = opts[:params]
-
if env["REQUEST_METHOD"] == "GET"
-
params = Utils.parse_nested_query(params) if params.is_a?(String)
-
params.update(Utils.parse_nested_query(env["QUERY_STRING"]))
-
env["QUERY_STRING"] = Utils.build_nested_query(params)
-
elsif !opts.has_key?(:input)
-
opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
-
if params.is_a?(Hash)
-
if data = Utils::Multipart.build_multipart(params)
-
opts[:input] = data
-
opts["CONTENT_LENGTH"] ||= data.length.to_s
-
opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Utils::Multipart::MULTIPART_BOUNDARY}"
-
else
-
opts[:input] = Utils.build_nested_query(params)
-
end
-
else
-
opts[:input] = params
-
end
-
end
-
end
-
-
1
empty_str = ""
-
1
empty_str.force_encoding("ASCII-8BIT") if empty_str.respond_to? :force_encoding
-
1
opts[:input] ||= empty_str
-
1
if String === opts[:input]
-
1
rack_input = StringIO.new(opts[:input])
-
else
-
rack_input = opts[:input]
-
end
-
-
1
rack_input.set_encoding(Encoding::BINARY) if rack_input.respond_to?(:set_encoding)
-
1
env['rack.input'] = rack_input
-
-
1
env["CONTENT_LENGTH"] ||= env["rack.input"].length.to_s
-
-
1
opts.each { |field, value|
-
1
env[field] = value if String === field
-
}
-
-
1
env
-
end
-
end
-
-
# Rack::MockResponse provides useful helpers for testing your apps.
-
# Usually, you don't create the MockResponse on your own, but use
-
# MockRequest.
-
-
1
class MockResponse < Rack::Response
-
# Headers
-
1
attr_reader :original_headers
-
-
# Errors
-
1
attr_accessor :errors
-
-
1
def initialize(status, headers, body, errors=StringIO.new(""))
-
@original_headers = headers
-
@errors = errors.string if errors.respond_to?(:string)
-
@body_string = nil
-
-
super(body, status, headers)
-
end
-
-
1
def =~(other)
-
body =~ other
-
end
-
-
1
def match(other)
-
body.match other
-
end
-
-
1
def body
-
# FIXME: apparently users of MockResponse expect the return value of
-
# MockResponse#body to be a string. However, the real response object
-
# returns the body as a list.
-
#
-
# See spec_showstatus.rb:
-
#
-
# should "not replace existing messages" do
-
# ...
-
# res.body.should == "foo!"
-
# end
-
super.join
-
end
-
-
1
def empty?
-
[201, 204, 304].include? status
-
end
-
end
-
end
-
1
module Rack
-
# A multipart form data parser, adapted from IOWA.
-
#
-
# Usually, Rack::Request#POST takes care of calling this.
-
1
module Multipart
-
1
autoload :UploadedFile, 'rack/multipart/uploaded_file'
-
1
autoload :Parser, 'rack/multipart/parser'
-
1
autoload :Generator, 'rack/multipart/generator'
-
-
1
EOL = "\r\n"
-
1
MULTIPART_BOUNDARY = "AaB03x"
-
1
MULTIPART = %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n
-
1
TOKEN = /[^\s()<>,;:\\"\/\[\]?=]+/
-
1
CONDISP = /Content-Disposition:\s*#{TOKEN}\s*/i
-
1
DISPPARM = /;\s*(#{TOKEN})=("(?:\\"|[^"])*"|#{TOKEN})*/
-
1
RFC2183 = /^#{CONDISP}(#{DISPPARM})+$/i
-
1
BROKEN_QUOTED = /^#{CONDISP}.*;\sfilename="(.*?)"(?:\s*$|\s*;\s*#{TOKEN}=)/i
-
1
BROKEN_UNQUOTED = /^#{CONDISP}.*;\sfilename=(#{TOKEN})/i
-
1
MULTIPART_CONTENT_TYPE = /Content-Type: (.*)#{EOL}/ni
-
1
MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:.*\s+name="?([^\";]*)"?/ni
-
1
MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{EOL}]*)/ni
-
-
1
class << self
-
1
def parse_multipart(env)
-
Parser.new(env).parse
-
end
-
-
1
def build_multipart(params, first = true)
-
Generator.new(params, first).dump
-
end
-
end
-
-
end
-
end
-
1
require 'rack/utils'
-
-
1
module Rack
-
# Rack::Request provides a convenient interface to a Rack
-
# environment. It is stateless, the environment +env+ passed to the
-
# constructor will be directly modified.
-
#
-
# req = Rack::Request.new(env)
-
# req.post?
-
# req.params["data"]
-
#
-
# The environment hash passed will store a reference to the Request object
-
# instantiated so that it will only instantiate if an instance of the Request
-
# object doesn't already exist.
-
-
1
class Request
-
# The environment of the request.
-
1
attr_reader :env
-
-
1
def initialize(env)
-
@env = env
-
end
-
-
1
def body; @env["rack.input"] end
-
1
def script_name; @env["SCRIPT_NAME"].to_s end
-
1
def path_info; @env["PATH_INFO"].to_s end
-
1
def request_method; @env["REQUEST_METHOD"] end
-
1
def query_string; @env["QUERY_STRING"].to_s end
-
1
def content_length; @env['CONTENT_LENGTH'] end
-
-
1
def content_type
-
content_type = @env['CONTENT_TYPE']
-
content_type.nil? || content_type.empty? ? nil : content_type
-
end
-
-
1
def session; @env['rack.session'] ||= {} end
-
1
def session_options; @env['rack.session.options'] ||= {} end
-
1
def logger; @env['rack.logger'] end
-
-
# The media type (type/subtype) portion of the CONTENT_TYPE header
-
# without any media type parameters. e.g., when CONTENT_TYPE is
-
# "text/plain;charset=utf-8", the media-type is "text/plain".
-
#
-
# For more information on the use of media types in HTTP, see:
-
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
-
1
def media_type
-
content_type && content_type.split(/\s*[;,]\s*/, 2).first.downcase
-
end
-
-
# The media type parameters provided in CONTENT_TYPE as a Hash, or
-
# an empty Hash if no CONTENT_TYPE or media-type parameters were
-
# provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8",
-
# this method responds with the following Hash:
-
# { 'charset' => 'utf-8' }
-
1
def media_type_params
-
return {} if content_type.nil?
-
Hash[*content_type.split(/\s*[;,]\s*/)[1..-1].
-
collect { |s| s.split('=', 2) }.
-
map { |k,v| [k.downcase, v] }.flatten]
-
end
-
-
# The character set of the request body if a "charset" media type
-
# parameter was given, or nil if no "charset" was specified. Note
-
# that, per RFC2616, text/* media types that specify no explicit
-
# charset are to be considered ISO-8859-1.
-
1
def content_charset
-
media_type_params['charset']
-
end
-
-
1
def scheme
-
if @env['HTTPS'] == 'on'
-
'https'
-
elsif @env['HTTP_X_FORWARDED_SSL'] == 'on'
-
'https'
-
elsif @env['HTTP_X_FORWARDED_PROTO']
-
@env['HTTP_X_FORWARDED_PROTO'].split(',')[0]
-
else
-
@env["rack.url_scheme"]
-
end
-
end
-
-
1
def ssl?
-
scheme == 'https'
-
end
-
-
1
def host_with_port
-
if forwarded = @env["HTTP_X_FORWARDED_HOST"]
-
forwarded.split(/,\s?/).last
-
else
-
@env['HTTP_HOST'] || "#{@env['SERVER_NAME'] || @env['SERVER_ADDR']}:#{@env['SERVER_PORT']}"
-
end
-
end
-
-
1
def port
-
if port = host_with_port.split(/:/)[1]
-
port.to_i
-
elsif port = @env['HTTP_X_FORWARDED_PORT']
-
port.to_i
-
elsif ssl?
-
443
-
elsif @env.has_key?("HTTP_X_FORWARDED_HOST")
-
80
-
else
-
@env["SERVER_PORT"].to_i
-
end
-
end
-
-
1
def host
-
# Remove port number.
-
host_with_port.to_s.gsub(/:\d+\z/, '')
-
end
-
-
1
def script_name=(s); @env["SCRIPT_NAME"] = s.to_s end
-
1
def path_info=(s); @env["PATH_INFO"] = s.to_s end
-
-
1
def delete?; request_method == "DELETE" end
-
1
def get?; request_method == "GET" end
-
1
def head?; request_method == "HEAD" end
-
1
def options?; request_method == "OPTIONS" end
-
1
def patch?; request_method == "PATCH" end
-
1
def post?; request_method == "POST" end
-
1
def put?; request_method == "PUT" end
-
1
def trace?; request_method == "TRACE" end
-
-
# The set of form-data media-types. Requests that do not indicate
-
# one of the media types presents in this list will not be eligible
-
# for form-data / param parsing.
-
1
FORM_DATA_MEDIA_TYPES = [
-
'application/x-www-form-urlencoded',
-
'multipart/form-data'
-
]
-
-
# The set of media-types. Requests that do not indicate
-
# one of the media types presents in this list will not be eligible
-
# for param parsing like soap attachments or generic multiparts
-
1
PARSEABLE_DATA_MEDIA_TYPES = [
-
'multipart/related',
-
'multipart/mixed'
-
]
-
-
# Determine whether the request body contains form-data by checking
-
# the request Content-Type for one of the media-types:
-
# "application/x-www-form-urlencoded" or "multipart/form-data". The
-
# list of form-data media types can be modified through the
-
# +FORM_DATA_MEDIA_TYPES+ array.
-
#
-
# A request body is also assumed to contain form-data when no
-
# Content-Type header is provided and the request_method is POST.
-
1
def form_data?
-
type = media_type
-
meth = env["rack.methodoverride.original_method"] || env['REQUEST_METHOD']
-
(meth == 'POST' && type.nil?) || FORM_DATA_MEDIA_TYPES.include?(type)
-
end
-
-
# Determine whether the request body contains data by checking
-
# the request media_type against registered parse-data media-types
-
1
def parseable_data?
-
PARSEABLE_DATA_MEDIA_TYPES.include?(media_type)
-
end
-
-
# Returns the data recieved in the query string.
-
1
def GET
-
if @env["rack.request.query_string"] == query_string
-
@env["rack.request.query_hash"]
-
else
-
@env["rack.request.query_string"] = query_string
-
@env["rack.request.query_hash"] = parse_query(query_string)
-
end
-
end
-
-
# Returns the data recieved in the request body.
-
#
-
# This method support both application/x-www-form-urlencoded and
-
# multipart/form-data.
-
1
def POST
-
if @env["rack.input"].nil?
-
raise "Missing rack.input"
-
elsif @env["rack.request.form_input"].eql? @env["rack.input"]
-
@env["rack.request.form_hash"]
-
elsif form_data? || parseable_data?
-
@env["rack.request.form_input"] = @env["rack.input"]
-
unless @env["rack.request.form_hash"] = parse_multipart(env)
-
form_vars = @env["rack.input"].read
-
-
# Fix for Safari Ajax postings that always append \0
-
# form_vars.sub!(/\0\z/, '') # performance replacement:
-
form_vars.slice!(-1) if form_vars[-1] == ?\0
-
-
@env["rack.request.form_vars"] = form_vars
-
@env["rack.request.form_hash"] = parse_query(form_vars)
-
-
@env["rack.input"].rewind
-
end
-
@env["rack.request.form_hash"]
-
else
-
{}
-
end
-
end
-
-
# The union of GET and POST data.
-
1
def params
-
@params ||= self.GET.merge(self.POST)
-
rescue EOFError
-
self.GET
-
end
-
-
# shortcut for request.params[key]
-
1
def [](key)
-
params[key.to_s]
-
end
-
-
# shortcut for request.params[key] = value
-
1
def []=(key, value)
-
params[key.to_s] = value
-
end
-
-
# like Hash#values_at
-
1
def values_at(*keys)
-
keys.map{|key| params[key] }
-
end
-
-
# the referer of the client
-
1
def referer
-
@env['HTTP_REFERER']
-
end
-
1
alias referrer referer
-
-
1
def user_agent
-
@env['HTTP_USER_AGENT']
-
end
-
-
1
def cookies
-
hash = @env["rack.request.cookie_hash"] ||= {}
-
string = @env["HTTP_COOKIE"]
-
-
hash.clear unless string
-
return hash if string == @env["rack.request.cookie_string"]
-
-
# According to RFC 2109:
-
# If multiple cookies satisfy the criteria above, they are ordered in
-
# the Cookie header such that those with more specific Path attributes
-
# precede those with less specific. Ordering with respect to other
-
# attributes (e.g., Domain) is unspecified.
-
Utils.parse_query(string, ';,').each { |k,v| hash[k] = Array === v ? v.first : v }
-
@env["rack.request.cookie_string"] = string
-
hash
-
rescue => error
-
raise error.class, "cannot parse Cookie header: #{error.message}"
-
end
-
-
1
def xhr?
-
@env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest"
-
end
-
-
1
def base_url
-
url = scheme + "://"
-
url << host
-
-
if scheme == "https" && port != 443 ||
-
scheme == "http" && port != 80
-
url << ":#{port}"
-
end
-
-
url
-
end
-
-
# Tries to return a remake of the original request URL as a string.
-
1
def url
-
base_url + fullpath
-
end
-
-
1
def path
-
script_name + path_info
-
end
-
-
1
def fullpath
-
query_string.empty? ? path : "#{path}?#{query_string}"
-
end
-
-
1
def accept_encoding
-
@env["HTTP_ACCEPT_ENCODING"].to_s.split(/,\s*/).map do |part|
-
m = /^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$/.match(part) # From WEBrick
-
-
if m
-
[m[1], (m[2] || 1.0).to_f]
-
else
-
raise "Invalid value for Accept-Encoding: #{part.inspect}"
-
end
-
end
-
end
-
-
1
def ip
-
if addr = @env['HTTP_X_FORWARDED_FOR']
-
(addr.split(',').grep(/\d\./).first || @env['REMOTE_ADDR']).to_s.strip
-
else
-
@env['REMOTE_ADDR']
-
end
-
end
-
-
1
protected
-
1
def parse_query(qs)
-
Utils.parse_nested_query(qs)
-
end
-
-
1
def parse_multipart(env)
-
Rack::Multipart.parse_multipart(env)
-
end
-
end
-
end
-
1
require 'rack/request'
-
1
require 'rack/utils'
-
1
require 'time'
-
-
1
module Rack
-
# Rack::Response provides a convenient interface to create a Rack
-
# response.
-
#
-
# It allows setting of headers and cookies, and provides useful
-
# defaults (a OK response containing HTML).
-
#
-
# You can use Response#write to iteratively generate your response,
-
# but note that this is buffered by Rack::Response until you call
-
# +finish+. +finish+ however can take a block inside which calls to
-
# +write+ are syncronous with the Rack response.
-
#
-
# Your application's +call+ should end returning Response#finish.
-
-
1
class Response
-
1
attr_accessor :length
-
-
1
def initialize(body=[], status=200, header={}, &block)
-
@status = status.to_i
-
@header = Utils::HeaderHash.new("Content-Type" => "text/html").
-
merge(header)
-
-
@chunked = "chunked" == @header['Transfer-Encoding']
-
@writer = lambda { |x| @body << x }
-
@block = nil
-
@length = 0
-
-
@body = []
-
-
if body.respond_to? :to_str
-
write body.to_str
-
elsif body.respond_to?(:each)
-
body.each { |part|
-
write part.to_s
-
}
-
else
-
raise TypeError, "stringable or iterable required"
-
end
-
-
yield self if block_given?
-
end
-
-
1
attr_reader :header
-
1
attr_accessor :status, :body
-
-
1
def [](key)
-
header[key]
-
end
-
-
1
def []=(key, value)
-
header[key] = value
-
end
-
-
1
def set_cookie(key, value)
-
Utils.set_cookie_header!(header, key, value)
-
end
-
-
1
def delete_cookie(key, value={})
-
Utils.delete_cookie_header!(header, key, value)
-
end
-
-
1
def redirect(target, status=302)
-
self.status = status
-
self["Location"] = target
-
end
-
-
1
def finish(&block)
-
@block = block
-
-
if [204, 304].include?(status.to_i)
-
header.delete "Content-Type"
-
header.delete "Content-Length"
-
[status.to_i, header, []]
-
else
-
[status.to_i, header, self]
-
end
-
end
-
1
alias to_a finish # For *response
-
1
alias to_ary finish # For implicit-splat on Ruby 1.9.2
-
-
1
def each(&callback)
-
@body.each(&callback)
-
@writer = callback
-
@block.call(self) if @block
-
end
-
-
# Append to body and update Content-Length.
-
#
-
# NOTE: Do not mix #write and direct #body access!
-
#
-
1
def write(str)
-
s = str.to_s
-
@length += Rack::Utils.bytesize(s) unless @chunked
-
@writer.call s
-
-
header["Content-Length"] = @length.to_s unless @chunked
-
str
-
end
-
-
1
def close
-
body.close if body.respond_to?(:close)
-
end
-
-
1
def empty?
-
@block == nil && @body.empty?
-
end
-
-
1
alias headers header
-
-
1
module Helpers
-
1
def invalid?; @status < 100 || @status >= 600; end
-
-
1
def informational?; @status >= 100 && @status < 200; end
-
1
def successful?; @status >= 200 && @status < 300; end
-
1
def redirection?; @status >= 300 && @status < 400; end
-
1
def client_error?; @status >= 400 && @status < 500; end
-
1
def server_error?; @status >= 500 && @status < 600; end
-
-
1
def ok?; @status == 200; end
-
1
def forbidden?; @status == 403; end
-
1
def not_found?; @status == 404; end
-
-
1
def redirect?; [301, 302, 303, 307].include? @status; end
-
-
# Headers
-
1
attr_reader :headers, :original_headers
-
-
1
def include?(header)
-
!!headers[header]
-
end
-
-
1
def content_type
-
headers["Content-Type"]
-
end
-
-
1
def content_length
-
cl = headers["Content-Length"]
-
cl ? cl.to_i : cl
-
end
-
-
1
def location
-
headers["Location"]
-
end
-
end
-
-
1
include Helpers
-
end
-
end
-
1
module Rack
-
# Sets an "X-Runtime" response header, indicating the response
-
# time of the request, in seconds
-
#
-
# You can put it right before the application to see the processing
-
# time, or before all the other middlewares to include time for them,
-
# too.
-
1
class Runtime
-
1
def initialize(app, name = nil)
-
1
@app = app
-
1
@header_name = "X-Runtime"
-
1
@header_name << "-#{name}" if name
-
end
-
-
1
def call(env)
-
start_time = Time.now
-
status, headers, body = @app.call(env)
-
request_time = Time.now - start_time
-
-
if !headers.has_key?(@header_name)
-
headers[@header_name] = "%0.6f" % request_time
-
end
-
-
[status, headers, body]
-
end
-
end
-
end
-
1
require 'rack/file'
-
-
1
module Rack
-
-
# = Sendfile
-
#
-
# The Sendfile middleware intercepts responses whose body is being
-
# served from a file and replaces it with a server specific X-Sendfile
-
# header. The web server is then responsible for writing the file contents
-
# to the client. This can dramatically reduce the amount of work required
-
# by the Ruby backend and takes advantage of the web server's optimized file
-
# delivery code.
-
#
-
# In order to take advantage of this middleware, the response body must
-
# respond to +to_path+ and the request must include an X-Sendfile-Type
-
# header. Rack::File and other components implement +to_path+ so there's
-
# rarely anything you need to do in your application. The X-Sendfile-Type
-
# header is typically set in your web servers configuration. The following
-
# sections attempt to document
-
#
-
# === Nginx
-
#
-
# Nginx supports the X-Accel-Redirect header. This is similar to X-Sendfile
-
# but requires parts of the filesystem to be mapped into a private URL
-
# hierarachy.
-
#
-
# The following example shows the Nginx configuration required to create
-
# a private "/files/" area, enable X-Accel-Redirect, and pass the special
-
# X-Sendfile-Type and X-Accel-Mapping headers to the backend:
-
#
-
# location ~ /files/(.*) {
-
# internal;
-
# alias /var/www/$1;
-
# }
-
#
-
# location / {
-
# proxy_redirect off;
-
#
-
# proxy_set_header Host $host;
-
# proxy_set_header X-Real-IP $remote_addr;
-
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
-
#
-
# proxy_set_header X-Sendfile-Type X-Accel-Redirect;
-
# proxy_set_header X-Accel-Mapping /var/www/=/files/;
-
#
-
# proxy_pass http://127.0.0.1:8080/;
-
# }
-
#
-
# Note that the X-Sendfile-Type header must be set exactly as shown above. The
-
# X-Accel-Mapping header should specify the internal URI path, followed by an
-
# equals sign (=), followed name of the location in the file system that it maps
-
# to. The middleware performs a simple substitution on the resulting path.
-
#
-
# See Also: http://wiki.codemongers.com/NginxXSendfile
-
#
-
# === lighttpd
-
#
-
# Lighttpd has supported some variation of the X-Sendfile header for some
-
# time, although only recent version support X-Sendfile in a reverse proxy
-
# configuration.
-
#
-
# $HTTP["host"] == "example.com" {
-
# proxy-core.protocol = "http"
-
# proxy-core.balancer = "round-robin"
-
# proxy-core.backends = (
-
# "127.0.0.1:8000",
-
# "127.0.0.1:8001",
-
# ...
-
# )
-
#
-
# proxy-core.allow-x-sendfile = "enable"
-
# proxy-core.rewrite-request = (
-
# "X-Sendfile-Type" => (".*" => "X-Sendfile")
-
# )
-
# }
-
#
-
# See Also: http://redmine.lighttpd.net/wiki/lighttpd/Docs:ModProxyCore
-
#
-
# === Apache
-
#
-
# X-Sendfile is supported under Apache 2.x using a separate module:
-
#
-
# https://tn123.org/mod_xsendfile/
-
#
-
# Once the module is compiled and installed, you can enable it using
-
# XSendFile config directive:
-
#
-
# RequestHeader Set X-Sendfile-Type X-Sendfile
-
# ProxyPassReverse / http://localhost:8001/
-
# XSendFile on
-
-
1
class Sendfile
-
1
F = ::File
-
-
1
def initialize(app, variation=nil)
-
1
@app = app
-
1
@variation = variation
-
end
-
-
1
def call(env)
-
status, headers, body = @app.call(env)
-
if body.respond_to?(:to_path)
-
case type = variation(env)
-
when 'X-Accel-Redirect'
-
path = F.expand_path(body.to_path)
-
if url = map_accel_path(env, path)
-
headers[type] = url
-
body = []
-
else
-
env['rack.errors'] << "X-Accel-Mapping header missing"
-
end
-
when 'X-Sendfile', 'X-Lighttpd-Send-File'
-
path = F.expand_path(body.to_path)
-
headers['Content-Length'] = '0'
-
headers[type] = path
-
body = []
-
when '', nil
-
else
-
env['rack.errors'] << "Unknown x-sendfile variation: '#{variation}'.\n"
-
end
-
end
-
[status, headers, body]
-
end
-
-
1
private
-
1
def variation(env)
-
@variation ||
-
env['sendfile.type'] ||
-
env['HTTP_X_SENDFILE_TYPE']
-
end
-
-
1
def map_accel_path(env, file)
-
if mapping = env['HTTP_X_ACCEL_MAPPING']
-
internal, external = mapping.split('=', 2).map{ |p| p.strip }
-
file.sub(/^#{internal}/i, external)
-
end
-
end
-
end
-
end
-
# AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
-
# bugrep: Andreas Zehnder
-
-
1
require 'time'
-
1
require 'rack/request'
-
1
require 'rack/response'
-
1
begin
-
1
require 'securerandom'
-
rescue LoadError
-
# We just won't get securerandom
-
end
-
-
1
module Rack
-
-
1
module Session
-
-
1
module Abstract
-
1
ENV_SESSION_KEY = 'rack.session'.freeze
-
1
ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze
-
-
# Thin wrapper around Hash that allows us to lazily load session id into session_options.
-
-
1
class OptionsHash < Hash #:nodoc:
-
1
def initialize(by, env, default_options)
-
@by = by
-
@env = env
-
@session_id_loaded = false
-
merge!(default_options)
-
end
-
-
1
def [](key)
-
load_session_id! if key == :id && session_id_not_loaded?
-
super
-
end
-
-
1
private
-
-
1
def session_id_not_loaded?
-
!key?(:id) && !@session_id_loaded
-
end
-
-
1
def load_session_id!
-
self[:id] = @by.send(:extract_session_id, @env)
-
@session_id_loaded = true
-
end
-
end
-
-
# SessionHash is responsible to lazily load the session from store.
-
-
1
class SessionHash < Hash
-
1
def initialize(by, env)
-
super()
-
@by = by
-
@env = env
-
@loaded = false
-
end
-
-
1
def [](key)
-
load_for_read!
-
super(key.to_s)
-
end
-
-
1
def has_key?(key)
-
load_for_read!
-
super(key.to_s)
-
end
-
1
alias :key? :has_key?
-
1
alias :include? :has_key?
-
-
1
def []=(key, value)
-
load_for_write!
-
super(key.to_s, value)
-
end
-
-
1
def clear
-
load_for_write!
-
super
-
end
-
-
1
def to_hash
-
load_for_read!
-
h = {}.replace(self)
-
h.delete_if { |k,v| v.nil? }
-
h
-
end
-
-
1
def update(hash)
-
load_for_write!
-
super(stringify_keys(hash))
-
end
-
-
1
def delete(key)
-
load_for_write!
-
super(key.to_s)
-
end
-
-
1
def inspect
-
load_for_read!
-
super
-
end
-
-
1
def exists?
-
return @exists if instance_variable_defined?(:@exists)
-
@exists = @by.send(:session_exists?, @env)
-
end
-
-
1
def loaded?
-
@loaded
-
end
-
-
1
private
-
-
1
def load_for_read!
-
load! if !loaded? && exists?
-
end
-
-
1
def load_for_write!
-
load! unless loaded?
-
end
-
-
1
def load!
-
id, session = @by.send(:load_session, @env)
-
@env[ENV_SESSION_OPTIONS_KEY][:id] = id
-
replace(stringify_keys(session))
-
@loaded = true
-
end
-
-
1
def stringify_keys(other)
-
hash = {}
-
other.each do |key, value|
-
hash[key.to_s] = value
-
end
-
hash
-
end
-
end
-
-
# ID sets up a basic framework for implementing an id based sessioning
-
# service. Cookies sent to the client for maintaining sessions will only
-
# contain an id reference. Only #get_session and #set_session are
-
# required to be overwritten.
-
#
-
# All parameters are optional.
-
# * :key determines the name of the cookie, by default it is
-
# 'rack.session'
-
# * :path, :domain, :expire_after, :secure, and :httponly set the related
-
# cookie options as by Rack::Response#add_cookie
-
# * :defer will not set a cookie in the response.
-
# * :renew (implementation dependent) will prompt the generation of a new
-
# session id, and migration of data to be referenced at the new id. If
-
# :defer is set, it will be overridden and the cookie will be set.
-
# * :sidbits sets the number of bits in length that a generated session
-
# id will be.
-
#
-
# These options can be set on a per request basis, at the location of
-
# env['rack.session.options']. Additionally the id of the session can be
-
# found within the options hash at the key :id. It is highly not
-
# recommended to change its value.
-
#
-
# Is Rack::Utils::Context compatible.
-
#
-
# Not included by default; you must require 'rack/session/abstract/id'
-
# to use.
-
-
1
class ID
-
1
DEFAULT_OPTIONS = {
-
:key => 'rack.session',
-
:path => '/',
-
:domain => nil,
-
:expire_after => nil,
-
:secure => false,
-
:httponly => true,
-
:defer => false,
-
:renew => false,
-
:sidbits => 128,
-
:cookie_only => true,
-
1
:secure_random => begin ::SecureRandom rescue false end
-
}
-
-
1
attr_reader :key, :default_options
-
-
1
def initialize(app, options={})
-
1
@app = app
-
1
@default_options = self.class::DEFAULT_OPTIONS.merge(options)
-
1
@key = options[:key] || "rack.session"
-
1
@cookie_only = @default_options.delete(:cookie_only)
-
1
initialize_sid
-
end
-
-
1
def call(env)
-
context(env)
-
end
-
-
1
def context(env, app=@app)
-
prepare_session(env)
-
status, headers, body = app.call(env)
-
commit_session(env, status, headers, body)
-
end
-
-
1
private
-
-
1
def initialize_sid
-
@sidbits = @default_options[:sidbits]
-
@sid_secure = @default_options[:secure_random]
-
@sid_length = @sidbits / 4
-
end
-
-
# Generate a new session id using Ruby #rand. The size of the
-
# session id is controlled by the :sidbits option.
-
# Monkey patch this to use custom methods for session id generation.
-
-
1
def generate_sid(secure = @sid_secure)
-
if secure
-
SecureRandom.hex(@sid_length)
-
else
-
"%0#{@sid_length}x" % Kernel.rand(2**@sidbits - 1)
-
end
-
rescue NotImplementedError
-
generate_sid(false)
-
end
-
-
# Sets the lazy session at 'rack.session' and places options and session
-
# metadata into 'rack.session.options'.
-
-
1
def prepare_session(env)
-
session_was = env[ENV_SESSION_KEY]
-
env[ENV_SESSION_KEY] = SessionHash.new(self, env)
-
env[ENV_SESSION_OPTIONS_KEY] = OptionsHash.new(self, env, @default_options)
-
env[ENV_SESSION_KEY].merge! session_was if session_was
-
end
-
-
# Extracts the session id from provided cookies and passes it and the
-
# environment to #get_session.
-
-
1
def load_session(env)
-
sid = current_session_id(env)
-
sid, session = get_session(env, sid)
-
[sid, session || {}]
-
end
-
-
# Extract session id from request object.
-
-
1
def extract_session_id(env)
-
request = Rack::Request.new(env)
-
sid = request.cookies[@key]
-
sid ||= request.params[@key] unless @cookie_only
-
sid
-
end
-
-
# Returns the current session id from the OptionsHash.
-
-
1
def current_session_id(env)
-
env[ENV_SESSION_OPTIONS_KEY][:id]
-
end
-
-
# Check if the session exists or not.
-
-
1
def session_exists?(env)
-
value = current_session_id(env)
-
value && !value.empty?
-
end
-
-
# Session should be commited if it was loaded, any of specific options like :renew, :drop
-
# or :expire_after was given and the security permissions match.
-
-
1
def commit_session?(env, session, options)
-
(loaded_session?(session) || force_options?(options)) && secure_session?(env, options)
-
end
-
-
1
def loaded_session?(session)
-
!session.is_a?(SessionHash) || session.loaded?
-
end
-
-
1
def force_options?(options)
-
options.values_at(:renew, :drop, :defer, :expire_after).any?
-
end
-
-
1
def secure_session?(env, options)
-
return true unless options[:secure]
-
request = Rack::Request.new(env)
-
request.ssl?
-
end
-
-
# Acquires the session from the environment and the session id from
-
# the session options and passes them to #set_session. If successful
-
# and the :defer option is not true, a cookie will be added to the
-
# response with the session's id.
-
-
1
def commit_session(env, status, headers, body)
-
session = env['rack.session']
-
options = env['rack.session.options']
-
-
if options[:drop] || options[:renew]
-
session_id = destroy_session(env, options[:id] || generate_sid, options)
-
return [status, headers, body] unless session_id
-
end
-
-
return [status, headers, body] unless commit_session?(env, session, options)
-
-
session.send(:load!) unless loaded_session?(session)
-
session = session.to_hash
-
session_id ||= options[:id] || generate_sid
-
-
if not data = set_session(env, session_id, session, options)
-
env["rack.errors"].puts("Warning! #{self.class.name} failed to save session. Content dropped.")
-
elsif options[:defer] and not options[:renew]
-
env["rack.errors"].puts("Defering cookie for #{session_id}") if $VERBOSE
-
else
-
cookie = Hash.new
-
cookie[:value] = data
-
cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after]
-
set_cookie(env, headers, cookie.merge!(options))
-
end
-
-
[status, headers, body]
-
end
-
-
# Sets the cookie back to the client with session id. We skip the cookie
-
# setting if the value didn't change (sid is the same) or expires was given.
-
-
1
def set_cookie(env, headers, cookie)
-
request = Rack::Request.new(env)
-
if request.cookies[@key] != cookie[:value] || cookie[:expires]
-
Utils.set_cookie_header!(headers, @key, cookie)
-
end
-
end
-
-
# All thread safety and session retrival proceedures should occur here.
-
# Should return [session_id, session].
-
# If nil is provided as the session id, generation of a new valid id
-
# should occur within.
-
-
1
def get_session(env, sid)
-
raise '#get_session not implemented.'
-
end
-
-
# All thread safety and session storage proceedures should occur here.
-
# Should return true or false dependant on whether or not the session
-
# was saved or not.
-
-
1
def set_session(env, sid, session, options)
-
raise '#set_session not implemented.'
-
end
-
-
# All thread safety and session destroy proceedures should occur here.
-
# Should return a new session id or nil if options[:drop]
-
-
1
def destroy_session(env, sid, options)
-
raise '#destroy_session not implemented'
-
end
-
end
-
end
-
end
-
end
-
1
require 'openssl'
-
1
require 'rack/request'
-
1
require 'rack/response'
-
1
require 'rack/session/abstract/id'
-
-
1
module Rack
-
-
1
module Session
-
-
# Rack::Session::Cookie provides simple cookie based session management.
-
# By default, the session is a Ruby Hash stored as base64 encoded marshalled
-
# data set to :key (default: rack.session). The object that encodes the
-
# session data is configurable and must respond to +encode+ and +decode+.
-
# Both methods must take a string and return a string.
-
#
-
# When the secret key is set, cookie data is checked for data integrity.
-
#
-
# Example:
-
#
-
# use Rack::Session::Cookie, :key => 'rack.session',
-
# :domain => 'foo.com',
-
# :path => '/',
-
# :expire_after => 2592000,
-
# :secret => 'change_me'
-
#
-
# All parameters are optional.
-
#
-
# Example of a cookie with no encoding:
-
#
-
# Rack::Session::Cookie.new(application, {
-
# :coder => Racke::Session::Cookie::Identity.new
-
# })
-
#
-
# Example of a cookie with custom encoding:
-
#
-
# Rack::Session::Cookie.new(application, {
-
# :coder => Class.new {
-
# def encode(str); str.reverse; end
-
# def decode(str); str.reverse; end
-
# }.new
-
# })
-
#
-
-
1
class Cookie < Abstract::ID
-
# Encode session cookies as Base64
-
1
class Base64
-
1
def encode(str)
-
[str].pack('m')
-
end
-
-
1
def decode(str)
-
str.unpack('m').first
-
end
-
-
# Encode session cookies as Marshaled Base64 data
-
1
class Marshal < Base64
-
1
def encode(str)
-
super(::Marshal.dump(str))
-
end
-
-
1
def decode(str)
-
::Marshal.load(super(str)) rescue nil
-
end
-
end
-
end
-
-
# Use no encoding for session cookies
-
1
class Identity
-
1
def encode(str); str; end
-
1
def decode(str); str; end
-
end
-
-
# Reverse string encoding. (trollface)
-
1
class Reverse
-
1
def encode(str); str.reverse; end
-
1
def decode(str); str.reverse; end
-
end
-
-
1
attr_reader :coder
-
-
1
def initialize(app, options={})
-
1
@secret = options[:secret]
-
1
@coder = options[:coder] ||= Base64::Marshal.new
-
1
super(app, options.merge!(:cookie_only => true))
-
end
-
-
1
private
-
-
1
def load_session(env)
-
data = unpacked_cookie_data(env)
-
data = persistent_session_id!(data)
-
[data["session_id"], data]
-
end
-
-
1
def extract_session_id(env)
-
unpacked_cookie_data(env)["session_id"]
-
end
-
-
1
def unpacked_cookie_data(env)
-
env["rack.session.unpacked_cookie_data"] ||= begin
-
request = Rack::Request.new(env)
-
session_data = request.cookies[@key]
-
-
if @secret && session_data
-
session_data, digest = session_data.split("--")
-
session_data = nil unless digest == generate_hmac(session_data)
-
end
-
-
coder.decode(session_data) || {}
-
end
-
end
-
-
1
def persistent_session_id!(data, sid=nil)
-
data ||= {}
-
data["session_id"] ||= sid || generate_sid
-
data
-
end
-
-
# Overwrite set cookie to bypass content equality and always stream the cookie.
-
-
1
def set_cookie(env, headers, cookie)
-
Utils.set_cookie_header!(headers, @key, cookie)
-
end
-
-
1
def set_session(env, session_id, session, options)
-
session = session.merge("session_id" => session_id)
-
session_data = coder.encode(session)
-
-
if @secret
-
session_data = "#{session_data}--#{generate_hmac(session_data)}"
-
end
-
-
if session_data.size > (4096 - @key.size)
-
env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K.")
-
nil
-
else
-
session_data
-
end
-
end
-
-
1
def destroy_session(env, session_id, options)
-
# Nothing to do here, data is in the client
-
generate_sid unless options[:drop]
-
end
-
-
1
def generate_hmac(data)
-
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, @secret, data)
-
end
-
-
end
-
end
-
end
-
1
module Rack
-
# Rack::URLMap takes a hash mapping urls or paths to apps, and
-
# dispatches accordingly. Support for HTTP/1.1 host names exists if
-
# the URLs start with <tt>http://</tt> or <tt>https://</tt>.
-
#
-
# URLMap modifies the SCRIPT_NAME and PATH_INFO such that the part
-
# relevant for dispatch is in the SCRIPT_NAME, and the rest in the
-
# PATH_INFO. This should be taken care of when you need to
-
# reconstruct the URL in order to create links.
-
#
-
# URLMap dispatches in such a way that the longest paths are tried
-
# first, since they are most specific.
-
-
1
class URLMap
-
1
NEGATIVE_INFINITY = -1.0 / 0.0
-
-
1
def initialize(map = {})
-
1
remap(map)
-
end
-
-
1
def remap(map)
-
1
longest_path_first = lambda do |(host, location, _, _)|
-
1
[host ? -host.size : NEGATIVE_INFINITY, -location.size]
-
end
-
1
@mapping = map.map { |location, app|
-
1
if location =~ %r{\Ahttps?://(.*?)(/.*)}
-
host, location = $1, $2
-
else
-
1
host = nil
-
end
-
-
1
unless location[0] == ?/
-
raise ArgumentError, "paths need to start with /"
-
end
-
1
location = location.chomp('/')
-
1
match = Regexp.new("^#{Regexp.quote(location).gsub('/', '/+')}(.*)", nil, 'n')
-
-
1
[host, location, match, app]
-
}.sort_by(&longest_path_first)
-
end
-
-
1
def call(env)
-
path = env["PATH_INFO"]
-
script_name = env['SCRIPT_NAME']
-
hHost, sName, sPort = env.values_at('HTTP_HOST','SERVER_NAME','SERVER_PORT')
-
@mapping.each { |host, location, match, app|
-
next unless (hHost == host || sName == host \
-
|| (host.nil? && (hHost == sName || hHost == sName+':'+sPort)))
-
next unless path.to_s =~ match && rest = $1
-
next unless rest.empty? || rest[0] == ?/
-
env.merge!('SCRIPT_NAME' => (script_name + location), 'PATH_INFO' => rest)
-
return app.call(env)
-
}
-
[404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found: #{path}"]]
-
ensure
-
env.merge! 'PATH_INFO' => path, 'SCRIPT_NAME' => script_name
-
end
-
end
-
end
-
-
# -*- encoding: binary -*-
-
1
require 'fileutils'
-
1
require 'set'
-
1
require 'tempfile'
-
1
require 'rack/multipart'
-
-
4
major, minor, patch = RUBY_VERSION.split('.').map { |v| v.to_i }
-
-
1
if major == 1 && minor < 9
-
require 'rack/backports/uri/common_18'
-
elsif major == 1 && minor == 9 && patch < 3
-
1
require 'rack/backports/uri/common_192'
-
else
-
require 'uri/common'
-
end
-
-
1
module Rack
-
# Rack::Utils contains a grab-bag of useful methods for writing web
-
# applications adopted from all kinds of Ruby libraries.
-
-
1
module Utils
-
# URI escapes. (CGI style space to +)
-
1
def escape(s)
-
URI.encode_www_form_component(s)
-
end
-
1
module_function :escape
-
-
# Like URI escaping, but with %20 instead of +. Strictly speaking this is
-
# true URI escaping.
-
1
def escape_path(s)
-
escape(s).gsub('+', '%20')
-
end
-
1
module_function :escape_path
-
-
# Unescapes a URI escaped string.
-
1
def unescape(s)
-
URI.decode_www_form_component(s)
-
end
-
1
module_function :unescape
-
-
1
DEFAULT_SEP = /[&;] */n
-
-
1
class << self
-
1
attr_accessor :key_space_limit
-
end
-
-
# The default number of bytes to allow parameter keys to take up.
-
# This helps prevent a rogue client from flooding a Request.
-
1
self.key_space_limit = 65536
-
-
# Stolen from Mongrel, with some small modifications:
-
# Parses a query string by breaking it up at the '&'
-
# and ';' characters. You can also use this to parse
-
# cookies by changing the characters used in the second
-
# parameter (which defaults to '&;').
-
1
def parse_query(qs, d = nil)
-
params = {}
-
-
max_key_space = Utils.key_space_limit
-
bytes = 0
-
-
(qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
-
k, v = p.split('=', 2).map { |x| unescape(x) }
-
-
if k
-
bytes += k.size
-
if bytes > max_key_space
-
raise RangeError, "exceeded available parameter key space"
-
end
-
end
-
-
if cur = params[k]
-
if cur.class == Array
-
params[k] << v
-
else
-
params[k] = [cur, v]
-
end
-
else
-
params[k] = v
-
end
-
end
-
-
return params
-
end
-
1
module_function :parse_query
-
-
1
def parse_nested_query(qs, d = nil)
-
params = {}
-
-
max_key_space = Utils.key_space_limit
-
bytes = 0
-
-
(qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
-
k, v = p.split('=', 2).map { |s| unescape(s) }
-
-
if k
-
bytes += k.size
-
if bytes > max_key_space
-
raise RangeError, "exceeded available parameter key space"
-
end
-
end
-
-
normalize_params(params, k, v)
-
end
-
-
return params
-
end
-
1
module_function :parse_nested_query
-
-
1
def normalize_params(params, name, v = nil)
-
name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
-
k = $1 || ''
-
after = $' || ''
-
-
return if k.empty?
-
-
if after == ""
-
params[k] = v
-
elsif after == "[]"
-
params[k] ||= []
-
raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
-
params[k] << v
-
elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$)
-
child_key = $1
-
params[k] ||= []
-
raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
-
if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key)
-
normalize_params(params[k].last, child_key, v)
-
else
-
params[k] << normalize_params({}, child_key, v)
-
end
-
else
-
params[k] ||= {}
-
raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash)
-
params[k] = normalize_params(params[k], after, v)
-
end
-
-
return params
-
end
-
1
module_function :normalize_params
-
-
1
def build_query(params)
-
params.map { |k, v|
-
if v.class == Array
-
build_query(v.map { |x| [k, x] })
-
else
-
"#{escape(k)}=#{escape(v)}"
-
end
-
}.join("&")
-
end
-
1
module_function :build_query
-
-
1
def build_nested_query(value, prefix = nil)
-
case value
-
when Array
-
value.map { |v|
-
build_nested_query(v, "#{prefix}[]")
-
}.join("&")
-
when Hash
-
value.map { |k, v|
-
build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
-
}.join("&")
-
when String
-
raise ArgumentError, "value must be a Hash" if prefix.nil?
-
"#{prefix}=#{escape(value)}"
-
else
-
prefix
-
end
-
end
-
1
module_function :build_nested_query
-
-
1
ESCAPE_HTML = {
-
"&" => "&",
-
"<" => "<",
-
">" => ">",
-
"'" => "'",
-
'"' => """,
-
"/" => "/"
-
}
-
1
if //.respond_to?(:encoding)
-
1
ESCAPE_HTML_PATTERN = Regexp.union(*ESCAPE_HTML.keys)
-
else
-
# On 1.8, there is a kcode = 'u' bug that allows for XSS otherwhise
-
# TODO doesn't apply to jruby, so a better condition above might be preferable?
-
ESCAPE_HTML_PATTERN = /#{Regexp.union(*ESCAPE_HTML.keys)}/n
-
end
-
-
# Escape ampersands, brackets and quotes to their HTML/XML entities.
-
1
def escape_html(string)
-
string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] }
-
end
-
1
module_function :escape_html
-
-
1
def select_best_encoding(available_encodings, accept_encoding)
-
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
-
-
expanded_accept_encoding =
-
accept_encoding.map { |m, q|
-
if m == "*"
-
(available_encodings - accept_encoding.map { |m2, _| m2 }).map { |m2| [m2, q] }
-
else
-
[[m, q]]
-
end
-
}.inject([]) { |mem, list|
-
mem + list
-
}
-
-
encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map { |m, _| m }
-
-
unless encoding_candidates.include?("identity")
-
encoding_candidates.push("identity")
-
end
-
-
expanded_accept_encoding.find_all { |m, q|
-
q == 0.0
-
}.each { |m, _|
-
encoding_candidates.delete(m)
-
}
-
-
return (encoding_candidates & available_encodings)[0]
-
end
-
1
module_function :select_best_encoding
-
-
1
def set_cookie_header!(header, key, value)
-
case value
-
when Hash
-
domain = "; domain=" + value[:domain] if value[:domain]
-
path = "; path=" + value[:path] if value[:path]
-
# According to RFC 2109, we need dashes here.
-
# N.B.: cgi.rb uses spaces...
-
expires = "; expires=" +
-
rfc2822(value[:expires].clone.gmtime) if value[:expires]
-
secure = "; secure" if value[:secure]
-
httponly = "; HttpOnly" if value[:httponly]
-
value = value[:value]
-
end
-
value = [value] unless Array === value
-
cookie = escape(key) + "=" +
-
value.map { |v| escape v }.join("&") +
-
"#{domain}#{path}#{expires}#{secure}#{httponly}"
-
-
case header["Set-Cookie"]
-
when nil, ''
-
header["Set-Cookie"] = cookie
-
when String
-
header["Set-Cookie"] = [header["Set-Cookie"], cookie].join("\n")
-
when Array
-
header["Set-Cookie"] = (header["Set-Cookie"] + [cookie]).join("\n")
-
end
-
-
nil
-
end
-
1
module_function :set_cookie_header!
-
-
1
def delete_cookie_header!(header, key, value = {})
-
case header["Set-Cookie"]
-
when nil, ''
-
cookies = []
-
when String
-
cookies = header["Set-Cookie"].split("\n")
-
when Array
-
cookies = header["Set-Cookie"]
-
end
-
-
cookies.reject! { |cookie|
-
if value[:domain]
-
cookie =~ /\A#{escape(key)}=.*domain=#{value[:domain]}/
-
else
-
cookie =~ /\A#{escape(key)}=/
-
end
-
}
-
-
header["Set-Cookie"] = cookies.join("\n")
-
-
set_cookie_header!(header, key,
-
{:value => '', :path => nil, :domain => nil,
-
:expires => Time.at(0) }.merge(value))
-
-
nil
-
end
-
1
module_function :delete_cookie_header!
-
-
# Return the bytesize of String; uses String#size under Ruby 1.8 and
-
# String#bytesize under 1.9.
-
1
if ''.respond_to?(:bytesize)
-
1
def bytesize(string)
-
string.bytesize
-
end
-
else
-
def bytesize(string)
-
string.size
-
end
-
end
-
1
module_function :bytesize
-
-
# Modified version of stdlib time.rb Time#rfc2822 to use '%d-%b-%Y' instead
-
# of '% %b %Y'.
-
# It assumes that the time is in GMT to comply to the RFC 2109.
-
#
-
# NOTE: I'm not sure the RFC says it requires GMT, but is ambigous enough
-
# that I'm certain someone implemented only that option.
-
# Do not use %a and %b from Time.strptime, it would use localized names for
-
# weekday and month.
-
#
-
1
def rfc2822(time)
-
wday = Time::RFC2822_DAY_NAME[time.wday]
-
mon = Time::RFC2822_MONTH_NAME[time.mon - 1]
-
time.strftime("#{wday}, %d-#{mon}-%Y %H:%M:%S GMT")
-
end
-
1
module_function :rfc2822
-
-
# Parses the "Range:" header, if present, into an array of Range objects.
-
# Returns nil if the header is missing or syntactically invalid.
-
# Returns an empty array if none of the ranges are satisfiable.
-
1
def byte_ranges(env, size)
-
# See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
-
http_range = env['HTTP_RANGE']
-
return nil unless http_range
-
ranges = []
-
http_range.split(/,\s*/).each do |range_spec|
-
matches = range_spec.match(/bytes=(\d*)-(\d*)/)
-
return nil unless matches
-
r0,r1 = matches[1], matches[2]
-
if r0.empty?
-
return nil if r1.empty?
-
# suffix-byte-range-spec, represents trailing suffix of file
-
r0 = [size - r1.to_i, 0].max
-
r1 = size - 1
-
else
-
r0 = r0.to_i
-
if r1.empty?
-
r1 = size - 1
-
else
-
r1 = r1.to_i
-
return nil if r1 < r0 # backwards range is syntactically invalid
-
r1 = size-1 if r1 >= size
-
end
-
end
-
ranges << (r0..r1) if r0 <= r1
-
end
-
ranges
-
end
-
1
module_function :byte_ranges
-
-
# Context allows the use of a compatible middleware at different points
-
# in a request handling stack. A compatible middleware must define
-
# #context which should take the arguments env and app. The first of which
-
# would be the request environment. The second of which would be the rack
-
# application that the request would be forwarded to.
-
1
class Context
-
1
attr_reader :for, :app
-
-
1
def initialize(app_f, app_r)
-
raise 'running context does not respond to #context' unless app_f.respond_to? :context
-
@for, @app = app_f, app_r
-
end
-
-
1
def call(env)
-
@for.context(env, @app)
-
end
-
-
1
def recontext(app)
-
self.class.new(@for, app)
-
end
-
-
1
def context(env, app=@app)
-
recontext(app).call(env)
-
end
-
end
-
-
# A case-insensitive Hash that preserves the original case of a
-
# header when set.
-
1
class HeaderHash < Hash
-
1
def self.new(hash={})
-
HeaderHash === hash ? hash : super(hash)
-
end
-
-
1
def initialize(hash={})
-
super()
-
@names = {}
-
hash.each { |k, v| self[k] = v }
-
end
-
-
1
def each
-
super do |k, v|
-
yield(k, v.respond_to?(:to_ary) ? v.to_ary.join("\n") : v)
-
end
-
end
-
-
1
def to_hash
-
hash = {}
-
each { |k,v| hash[k] = v }
-
hash
-
end
-
-
1
def [](k)
-
super(k) || super(@names[k.downcase])
-
end
-
-
1
def []=(k, v)
-
canonical = k.downcase
-
delete k if @names[canonical] && @names[canonical] != k # .delete is expensive, don't invoke it unless necessary
-
@names[k] = @names[canonical] = k
-
super k, v
-
end
-
-
1
def delete(k)
-
canonical = k.downcase
-
result = super @names.delete(canonical)
-
@names.delete_if { |name,| name.downcase == canonical }
-
result
-
end
-
-
1
def include?(k)
-
@names.include?(k) || @names.include?(k.downcase)
-
end
-
-
1
alias_method :has_key?, :include?
-
1
alias_method :member?, :include?
-
1
alias_method :key?, :include?
-
-
1
def merge!(other)
-
other.each { |k, v| self[k] = v }
-
self
-
end
-
-
1
def merge(other)
-
hash = dup
-
hash.merge! other
-
end
-
-
1
def replace(other)
-
clear
-
other.each { |k, v| self[k] = v }
-
self
-
end
-
end
-
-
# Every standard HTTP code mapped to the appropriate message.
-
# Generated with:
-
# curl -s http://www.iana.org/assignments/http-status-codes | \
-
# ruby -ane 'm = /^(\d{3}) +(\S[^\[(]+)/.match($_) and
-
# puts " #{m[1]} => \x27#{m[2].strip}x27,"'
-
1
HTTP_STATUS_CODES = {
-
100 => 'Continue',
-
101 => 'Switching Protocols',
-
102 => 'Processing',
-
200 => 'OK',
-
201 => 'Created',
-
202 => 'Accepted',
-
203 => 'Non-Authoritative Information',
-
204 => 'No Content',
-
205 => 'Reset Content',
-
206 => 'Partial Content',
-
207 => 'Multi-Status',
-
226 => 'IM Used',
-
300 => 'Multiple Choices',
-
301 => 'Moved Permanently',
-
302 => 'Found',
-
303 => 'See Other',
-
304 => 'Not Modified',
-
305 => 'Use Proxy',
-
306 => 'Reserved',
-
307 => 'Temporary Redirect',
-
400 => 'Bad Request',
-
401 => 'Unauthorized',
-
402 => 'Payment Required',
-
403 => 'Forbidden',
-
404 => 'Not Found',
-
405 => 'Method Not Allowed',
-
406 => 'Not Acceptable',
-
407 => 'Proxy Authentication Required',
-
408 => 'Request Timeout',
-
409 => 'Conflict',
-
410 => 'Gone',
-
411 => 'Length Required',
-
412 => 'Precondition Failed',
-
413 => 'Request Entity Too Large',
-
414 => 'Request-URI Too Long',
-
415 => 'Unsupported Media Type',
-
416 => 'Requested Range Not Satisfiable',
-
417 => 'Expectation Failed',
-
422 => 'Unprocessable Entity',
-
423 => 'Locked',
-
424 => 'Failed Dependency',
-
426 => 'Upgrade Required',
-
500 => 'Internal Server Error',
-
501 => 'Not Implemented',
-
502 => 'Bad Gateway',
-
503 => 'Service Unavailable',
-
504 => 'Gateway Timeout',
-
505 => 'HTTP Version Not Supported',
-
506 => 'Variant Also Negotiates',
-
507 => 'Insufficient Storage',
-
510 => 'Not Extended',
-
}
-
-
# Responses with HTTP status codes that should not have an entity body
-
1
STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 304)
-
-
1
SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
-
51
[message.downcase.gsub(/\s|-/, '_').to_sym, code]
-
}.flatten]
-
-
1
def status_code(status)
-
if status.is_a?(Symbol)
-
SYMBOL_TO_STATUS_CODE[status] || 500
-
else
-
status.to_i
-
end
-
end
-
1
module_function :status_code
-
-
1
Multipart = Rack::Multipart
-
-
end
-
end
-
1
require 'rack'
-
-
1
module Rack #:nodoc:
-
# A stackable dynamic tree based Rack router.
-
#
-
# Rack::Mount supports Rack's Cascade style of trying several routes until
-
# it finds one that is not a 404. This allows multiple routes to be nested
-
# or stacked on top of each other. Since the application endpoint can
-
# trigger the router to continue matching, middleware can be used to add
-
# arbitrary conditions to any route. This allows you to route based on
-
# other request attributes, session information, or even data dynamically
-
# pulled from a database.
-
1
module Mount
-
1
autoload :CodeGeneration, 'rack/mount/code_generation'
-
1
autoload :GeneratableRegexp, 'rack/mount/generatable_regexp'
-
1
autoload :Multimap, 'rack/mount/multimap'
-
1
autoload :Prefix, 'rack/mount/prefix'
-
1
autoload :RegexpWithNamedGroups, 'rack/mount/regexp_with_named_groups'
-
1
autoload :Route, 'rack/mount/route'
-
1
autoload :RouteSet, 'rack/mount/route_set'
-
1
autoload :RoutingError, 'rack/mount/route_set'
-
1
autoload :Strexp, 'rack/mount/strexp'
-
1
autoload :Utils, 'rack/mount/utils'
-
1
autoload :Version, 'rack/mount/version'
-
-
1
module Analysis #:nodoc:
-
1
autoload :Histogram, 'rack/mount/analysis/histogram'
-
1
autoload :Splitting, 'rack/mount/analysis/splitting'
-
end
-
end
-
end
-
1
module Rack::Mount
-
1
module Analysis
-
1
class Histogram < Hash #:nodoc:
-
1
attr_reader :count
-
-
1
def initialize
-
2
@count = 0
-
2
super(0)
-
2
expire_caches!
-
end
-
-
1
def <<(value)
-
61
@count += 1
-
61
self[value] += 1 if value
-
61
expire_caches!
-
61
self
-
end
-
-
1
def sorted_by_frequency
-
5
sort_by { |_, value| value }.reverse!
-
end
-
-
1
def max
-
@max ||= values.max || 0
-
end
-
-
1
def min
-
@min ||= values.min || 0
-
end
-
-
1
def mean
-
5
@mean ||= calculate_mean
-
end
-
-
1
def standard_deviation
-
1
@standard_deviation ||= calculate_standard_deviation
-
end
-
-
1
def upper_quartile_limit
-
4
@upper_quartile_limit ||= calculate_upper_quartile_limit
-
end
-
-
1
def keys_in_upper_quartile
-
1
@keys_in_upper_quartile ||= compute_keys_in_upper_quartile
-
end
-
-
1
private
-
1
def calculate_mean
-
1
count / size
-
end
-
-
1
def calculate_variance
-
5
values.inject(0) { |sum, e| sum + (e - mean) ** 2 } / count.to_f
-
end
-
-
1
def calculate_standard_deviation
-
1
Math.sqrt(calculate_variance)
-
end
-
-
1
def calculate_upper_quartile_limit
-
1
mean + standard_deviation
-
end
-
-
1
def compute_keys_in_upper_quartile
-
7
sorted_by_frequency.select { |_, value| value >= upper_quartile_limit }.map! { |key, _| key }
-
end
-
-
1
def expire_caches!
-
63
@max = @min = @mean = @standard_deviation = nil
-
63
@keys_in_upper_quartile = nil
-
end
-
end
-
end
-
end
-
1
require 'rack/mount/utils'
-
-
1
module Rack::Mount
-
1
module Analysis
-
1
class Splitting
-
1
NULL = "\0"
-
-
1
class Key < Struct.new(:method, :index, :separators)
-
1
def self.split(value, separator_pattern)
-
keys = value.split(separator_pattern)
-
keys.shift if keys[0] == ''
-
keys << NULL
-
keys
-
end
-
-
1
def call(cache, obj)
-
(cache[method] ||= self.class.split(obj.send(method), separators))[index]
-
end
-
-
1
def call_source(cache, obj)
-
1
"(#{cache}[:#{method}] ||= Analysis::Splitting::Key.split(#{obj}.#{method}, #{separators.inspect}))[#{index}]"
-
end
-
-
1
def inspect
-
1
"#{method}[#{index}].split(#{separators.inspect})"
-
end
-
end
-
-
1
def initialize(*keys)
-
2
clear
-
2
keys.each { |key| self << key }
-
end
-
-
1
def clear
-
2
@raw_keys = []
-
2
@key_frequency = Analysis::Histogram.new
-
2
self
-
end
-
-
1
def <<(key)
-
23
raise ArgumentError unless key.is_a?(Hash)
-
23
@raw_keys << key
-
nil
-
end
-
-
1
def possible_keys
-
@possible_keys ||= begin
-
1
@raw_keys.map do |key|
-
23
key.inject({}) { |requirements, (method, requirement)|
-
44
process_key(requirements, method, requirement)
-
44
requirements
-
}
-
end
-
47
end
-
end
-
-
1
def report
-
@report ||= begin
-
85
possible_keys.each { |keys| keys.each_pair { |key, _| @key_frequency << key } }
-
1
return [] if @key_frequency.count <= 1
-
1
@key_frequency.keys_in_upper_quartile
-
1
end
-
end
-
-
1
def expire!
-
25
@possible_keys = @report = nil
-
end
-
-
1
def process_key(requirements, method, requirement)
-
44
separators = separators(method)
-
44
if requirement.is_a?(Regexp) && separators.any?
-
23
generate_split_keys(requirement, separators).each_with_index do |value, index|
-
40
requirements[Key.new(method, index, Regexp.union(*separators))] = value
-
end
-
else
-
21
if requirement.is_a?(Regexp)
-
21
expression = Utils.parse_regexp(requirement)
-
-
21
if expression.is_a?(Regin::Expression) && expression.anchored_to_line?
-
134
expression = Regin::Expression.new(expression.reject { |e| e.is_a?(Regin::Anchor) })
-
21
return requirements[method] = expression.to_s if expression.literal?
-
end
-
end
-
-
requirements[method] = requirement
-
end
-
end
-
-
1
private
-
1
def separators(key)
-
44
key == :path_info ? ["/", "."] : []
-
end
-
-
1
def generate_split_keys(regexp, separators) #:nodoc:
-
23
segments = []
-
23
buf = nil
-
23
parts = Utils.parse_regexp(regexp)
-
23
parts.each_with_index do |part, index|
-
325
case part
-
when Regin::Anchor
-
24
if part.value == '$' || part.value == '\Z'
-
1
segments << join_buffer(buf, regexp) if buf
-
1
segments << NULL
-
1
buf = nil
-
1
break
-
end
-
when Regin::CharacterClass
-
break if separators.any? { |s| part.include?(s) }
-
buf = nil
-
segments << part.to_regexp(true)
-
when Regin::Character
-
757
if separators.any? { |s| part.include?(s) }
-
41
segments << join_buffer(buf, regexp) if buf
-
41
peek = parts[index+1]
-
41
if peek.is_a?(Regin::Character) && separators.include?(peek.value)
-
segments << ''
-
end
-
41
buf = nil
-
else
-
225
buf ||= Regin::Expression.new([])
-
225
buf += [part]
-
end
-
when Regin::Group
-
35
if part.quantifier == '?'
-
21
value = part.expression.first
-
63
if separators.any? { |s| value.include?(s) }
-
21
segments << join_buffer(buf, regexp) if buf
-
21
buf = nil
-
end
-
21
break
-
elsif part.quantifier == nil
-
42
break if separators.any? { |s| part.include?(s) }
-
14
buf = nil
-
14
segments << part.to_regexp(true)
-
else
-
break
-
end
-
else
-
break
-
end
-
end
-
-
23
while segments.length > 0 && (segments.last.nil? || segments.last == '')
-
segments.pop
-
end
-
-
23
segments
-
end
-
-
1
def join_buffer(parts, regexp)
-
25
if parts.literal?
-
25
parts.to_s
-
else
-
parts.to_regexp(true)
-
end
-
end
-
end
-
end
-
end
-
1
module Rack::Mount
-
1
module CodeGeneration #:nodoc:
-
1
def _expired_recognize(env) #:nodoc:
-
raise 'route set not finalized'
-
end
-
-
1
def rehash
-
1
super
-
1
optimize_recognize!
-
end
-
-
1
private
-
1
def expire!
-
25
if @optimized_recognize_defined
-
remove_metaclass_method :recognize
-
-
class << self
-
alias_method :recognize, :_expired_recognize
-
end
-
-
@optimized_recognize_defined = false
-
end
-
-
25
super
-
end
-
-
1
def optimize_container_iterator(container)
-
Utils.debug "optimizing container - size #{container.size}"
-
-
body = []
-
-
container.each_with_index { |route, i|
-
body << "route = self[#{i}]"
-
body << 'matches = {}'
-
body << 'params = route.defaults.dup'
-
-
conditions = []
-
route.conditions.each do |method, condition|
-
b = []
-
if condition.is_a?(Regexp)
-
b << "if m = obj.#{method}.match(#{condition.inspect})"
-
b << "matches[:#{method}] = m"
-
if (named_captures = route.named_captures[method]) && named_captures.any?
-
b << 'captures = m.captures'
-
b << 'p = nil'
-
b << named_captures.map { |k, j| "params[#{k.inspect}] = p if p = captures[#{j}]" }.join('; ')
-
end
-
else
-
b << "if m = obj.#{method} == route.conditions[:#{method}]"
-
end
-
b << 'true'
-
b << 'end'
-
conditions << "(#{b.join('; ')})"
-
end
-
-
body << <<-RUBY
-
if #{conditions.join(' && ')}
-
yield route, matches, params
-
end
-
RUBY
-
}
-
-
container.instance_eval(<<-RUBY, __FILE__, __LINE__)
-
def optimized_each(obj)
-
#{body.join("\n")}
-
nil
-
end
-
RUBY
-
end
-
-
1
def optimize_recognize!
-
1
Utils.debug "optimizing recognize"
-
-
1
uses_cache = false
-
-
1
keys = @recognition_keys.map { |key|
-
2
if key.respond_to?(:call_source)
-
1
uses_cache = true
-
1
key.call_source(:cache, :obj)
-
else
-
1
"obj.#{key}"
-
end
-
}.join(', ')
-
-
1
@optimized_recognize_defined = true
-
-
1
remove_metaclass_method :recognize
-
-
1
instance_eval(<<-RUBY, __FILE__, __LINE__)
-
def recognize(obj)
-
#{"cache = {}" if uses_cache}
-
container = @recognition_graph[#{keys}]
-
optimize_container_iterator(container) unless container.respond_to?(:optimized_each)
-
-
if block_given?
-
container.optimized_each(obj) do |route, matches, params|
-
yield route, matches, params
-
end
-
else
-
container.optimized_each(obj) do |route, matches, params|
-
return route, matches, params
-
end
-
end
-
-
nil
-
end
-
RUBY
-
end
-
-
# method_defined? can't distinguish between instance
-
# and meta methods. So we have to rescue if the method
-
# has not been defined in the metaclass yet.
-
1
def remove_metaclass_method(symbol)
-
2
metaclass = class << self; self; end
-
2
Utils.silence_debug { metaclass.send(:remove_method, symbol) }
-
rescue NameError
-
1
nil
-
end
-
end
-
end
-
1
require 'rack/mount/utils'
-
-
1
module Rack::Mount
-
1
class GeneratableRegexp < Regexp #:nodoc:
-
1
class DynamicSegment #:nodoc:
-
1
attr_reader :name, :requirement
-
-
1
def initialize(name, requirement)
-
35
@name, @requirement = name.to_sym, requirement
-
35
freeze
-
end
-
-
1
def ==(obj)
-
@name == obj.name && @requirement == obj.requirement
-
end
-
-
1
def =~(str)
-
@requirement =~ str
-
end
-
-
1
def to_hash
-
35
{ @name => @requirement }
-
end
-
-
1
def inspect
-
"/(?<#{@name}>#{@requirement.source})/"
-
end
-
end
-
-
1
module InstanceMethods
-
1
def self.extended(obj)
-
44
obj.segments
-
end
-
-
1
def defaults=(defaults)
-
44
@required_captures = nil
-
44
@required_params = nil
-
44
@required_defaults = nil
-
44
@defaults = defaults
-
end
-
-
1
def defaults
-
44
@defaults ||= {}
-
end
-
-
1
def generatable?
-
segments.any?
-
end
-
-
1
def generate(params = {}, recall = {}, options = {})
-
return nil unless generatable?
-
-
merged = recall.merge(params)
-
return nil unless required_params.all? { |p| merged.include?(p) }
-
return nil unless required_defaults.all? { |k, v| merged[k] == v }
-
-
generate_from_segments(segments, params, merged, options)
-
end
-
-
1
def segments
-
@segments ||= begin
-
44
defaults
-
44
segments = []
-
44
catch(:halt) do
-
44
expression = Utils.parse_regexp(self)
-
44
segments = parse_segments(expression)
-
end
-
44
segments
-
220
end
-
end
-
-
1
def captures
-
292
segments.flatten.find_all { |s| s.is_a?(DynamicSegment) }
-
end
-
-
1
def required_captures
-
@required_captures ||= segments.find_all { |s|
-
81
s.is_a?(DynamicSegment) && !@defaults.include?(s.name)
-
88
}.freeze
-
end
-
-
1
def required_params
-
81
@required_params ||= required_captures.map { |s| s.name }.freeze
-
end
-
-
1
def required_defaults
-
@required_defaults ||= begin
-
44
required_defaults = @defaults.dup
-
79
captures.inject({}) { |h, s| h.merge!(s.to_hash) }.keys.each { |name|
-
35
required_defaults.delete(name)
-
}
-
44
required_defaults
-
97
end
-
end
-
-
1
def freeze
-
44
segments
-
44
captures
-
44
required_captures
-
44
required_params
-
44
required_defaults
-
44
super
-
end
-
-
1
private
-
1
def parse_segments(segments)
-
65
s = []
-
65
segments.each_with_index do |part, index|
-
501
case part
-
when Regin::Anchor
-
# ignore
-
when Regin::Character
-
358
throw :halt unless part.literal?
-
-
358
if s.last.is_a?(String)
-
291
s.last << part.value.dup
-
else
-
67
s << part.value.dup
-
end
-
when Regin::Group
-
56
if part.name
-
35
s << DynamicSegment.new(part.name, part.expression.to_regexp(true))
-
else
-
21
s << parse_segments(part.expression)
-
end
-
when Regin::Expression
-
return parse_segments(part)
-
else
-
throw :halt
-
end
-
end
-
-
65
s
-
end
-
-
1
EMPTY_STRING = ''.freeze
-
-
1
def generate_from_segments(segments, params, merged, options, optional = false)
-
if optional
-
return EMPTY_STRING if segments.all? { |s| s.is_a?(String) }
-
return EMPTY_STRING unless segments.flatten.any? { |s|
-
params.has_key?(s.name) if s.is_a?(DynamicSegment)
-
}
-
return EMPTY_STRING if segments.any? { |segment|
-
if segment.is_a?(DynamicSegment)
-
value = merged[segment.name] || @defaults[segment.name]
-
value = parameterize(segment.name, value, options)
-
-
merged_value = parameterize(segment.name, merged[segment.name], options)
-
default_value = parameterize(segment.name, @defaults[segment.name], options)
-
-
if value.nil? || segment !~ value
-
true
-
elsif merged_value == default_value
-
# Nasty control flow
-
return :clear_remaining_segments
-
else
-
false
-
end
-
end
-
}
-
end
-
-
generated = segments.map do |segment|
-
case segment
-
when String
-
segment
-
when DynamicSegment
-
value = params[segment.name] || merged[segment.name] || @defaults[segment.name]
-
value = parameterize(segment.name, value, options)
-
if value && segment =~ value.to_s
-
value
-
else
-
return
-
end
-
when Array
-
value = generate_from_segments(segment, params, merged, options, true)
-
if value == :clear_remaining_segments
-
segment.each { |s| params.delete(s.name) if s.is_a?(DynamicSegment) }
-
EMPTY_STRING
-
elsif value.nil?
-
EMPTY_STRING
-
else
-
value
-
end
-
end
-
end
-
-
# Delete any used items from the params
-
segments.each { |s| params.delete(s.name) if s.is_a?(DynamicSegment) }
-
-
generated.join
-
end
-
-
1
def parameterize(name, value, options)
-
if block = options[:parameterize]
-
block.call(name, value)
-
else
-
value
-
end
-
end
-
end
-
1
include InstanceMethods
-
-
1
def initialize(regexp)
-
super
-
segments
-
end
-
end
-
end
-
1
module Rack::Mount
-
1
class Multimap #:nodoc:
-
1
def initialize(default = [])
-
15
@hash = Hash.new(default)
-
end
-
-
1
def initialize_copy(original)
-
@hash = Hash.new(original.default.dup)
-
original.hash.each_pair do |key, container|
-
@hash[key] = container.dup
-
end
-
end
-
-
1
def store(*args)
-
88
keys = args.dup
-
88
value = keys.pop
-
88
key = keys.shift
-
-
88
raise ArgumentError, 'wrong number of arguments (1 for 2)' unless value
-
-
88
unless key.respond_to?(:=~)
-
raise ArgumentError, "unsupported key: #{args.first.inspect}"
-
end
-
-
88
if key.is_a?(Regexp)
-
if keys.empty?
-
@hash.each_pair { |k, l| l << value if k =~ key }
-
self.default << value
-
else
-
@hash.each_pair { |k, _|
-
if k =~ key
-
args[0] = k
-
_store(*args)
-
end
-
}
-
-
self.default = self.class.new(default) unless default.is_a?(self.class)
-
default[*keys.dup] = value
-
end
-
else
-
88
_store(*args)
-
end
-
end
-
1
alias_method :[]=, :store
-
-
1
def [](*keys)
-
i, l, r, k = 0, keys.length, self, self.class
-
while r.is_a?(k)
-
r = i < l ? r.hash[keys[i]] : r.default
-
i += 1
-
end
-
r
-
end
-
-
1
def height
-
containers_with_default.max { |a, b| a.length <=> b.length }.length
-
end
-
-
1
def average_height
-
lengths = containers_with_default.map { |e| e.length }
-
lengths.inject(0) { |sum, len| sum += len }.to_f / lengths.size
-
end
-
-
1
def containers_with_default
-
containers = []
-
each_container_with_default { |container| containers << container }
-
containers
-
end
-
-
1
protected
-
1
def _store(*args)
-
88
keys = args
-
88
value = args.pop
-
-
88
raise ArgumentError, 'wrong number of arguments (1 for 2)' unless value
-
-
88
if keys.length > 1
-
43
update_container(keys.shift) do |container|
-
43
container = self.class.new(container) unless container.is_a?(self.class)
-
43
container[*keys] = value
-
43
container
-
end
-
45
elsif keys.length == 1
-
44
update_container(keys.first) do |container|
-
44
container << value
-
44
container
-
end
-
else
-
1
self << value
-
end
-
end
-
-
1
def <<(value)
-
1
@hash.each_value { |container| container << value }
-
1
self.default << value
-
1
self
-
end
-
-
1
def each_container_with_default(&block)
-
@hash.each_value do |container|
-
iterate_over_container(container, &block)
-
end
-
iterate_over_container(default, &block)
-
self
-
end
-
-
1
def default
-
88
@hash.default
-
end
-
-
1
def default=(value)
-
@hash.default = value
-
end
-
-
1
def hash
-
@hash
-
end
-
-
1
private
-
1
def update_container(key)
-
87
container = @hash[key]
-
87
container = container.dup if container.equal?(default)
-
87
container = yield(container)
-
87
@hash[key] = container
-
end
-
-
1
def iterate_over_container(container)
-
if container.respond_to?(:each_container_with_default)
-
container.each_container_with_default do |value|
-
yield value
-
end
-
else
-
yield container
-
end
-
end
-
end
-
end
-
1
require 'rack/mount/utils'
-
-
1
module Rack::Mount
-
1
class Prefix #:nodoc:
-
1
EMPTY_STRING = ''.freeze
-
1
PATH_INFO = 'PATH_INFO'.freeze
-
1
SCRIPT_NAME = 'SCRIPT_NAME'.freeze
-
1
SLASH = '/'.freeze
-
-
1
KEY = 'rack.mount.prefix'.freeze
-
-
1
def initialize(app, prefix = nil)
-
1
@app, @prefix = app, prefix
-
1
freeze
-
end
-
-
1
def call(env)
-
if prefix = env[KEY] || @prefix
-
old_path_info = env[PATH_INFO].dup
-
old_script_name = env[SCRIPT_NAME].dup
-
-
begin
-
env[PATH_INFO] = env[PATH_INFO].sub(prefix, EMPTY_STRING)
-
env[PATH_INFO] = EMPTY_STRING if env[PATH_INFO] == SLASH
-
env[SCRIPT_NAME] = Utils.normalize_path(env[SCRIPT_NAME].to_s + prefix)
-
@app.call(env)
-
ensure
-
env[PATH_INFO] = old_path_info
-
env[SCRIPT_NAME] = old_script_name
-
end
-
else
-
@app.call(env)
-
end
-
end
-
end
-
end
-
1
module Rack::Mount
-
1
if Regin.regexp_supports_named_captures?
-
1
RegexpWithNamedGroups = Regexp
-
else
-
require 'strscan'
-
-
# A wrapper that adds shim named capture support to older
-
# versions of Ruby.
-
#
-
# Because the named capture syntax causes a parse error, an
-
# alternate syntax is used to indicate named captures.
-
#
-
# Ruby 1.9+ named capture syntax:
-
#
-
# /(?<foo>[a-z]+)/
-
#
-
# Ruby 1.8 shim syntax:
-
#
-
# /(?:<foo>[a-z]+)/
-
class RegexpWithNamedGroups < Regexp
-
def self.new(regexp) #:nodoc:
-
if regexp.is_a?(RegexpWithNamedGroups)
-
regexp
-
else
-
super
-
end
-
end
-
-
# Wraps Regexp with named capture support.
-
def initialize(regexp)
-
regexp = Regexp.compile(regexp) unless regexp.is_a?(Regexp)
-
source, options = regexp.source, regexp.options
-
@names, scanner = [], StringScanner.new(source)
-
-
while scanner.skip_until(/\(/)
-
if scanner.scan(/\?:<([^>]+)>/)
-
@names << scanner[1]
-
elsif scanner.scan(/\?(i?m?x?\-?i?m?x?)?:/)
-
# ignore noncapture
-
else
-
@names << nil
-
end
-
end
-
source.gsub!(/\?:<([^>]+)>/, '')
-
-
@names = [] unless @names.any?
-
@names.freeze
-
-
super(source, options)
-
end
-
-
def names
-
@names.dup
-
end
-
-
def named_captures
-
named_captures = {}
-
names.each_with_index { |n, i|
-
named_captures[n] = [i+1] if n
-
}
-
named_captures
-
end
-
-
def eql?(other)
-
super && @names.eql?(other.names)
-
end
-
end
-
end
-
end
-
1
require 'rack/mount/generatable_regexp'
-
1
require 'rack/mount/regexp_with_named_groups'
-
1
require 'rack/mount/utils'
-
-
1
module Rack::Mount
-
# Route is an internal class used to wrap a single route attributes.
-
#
-
# Plugins should not depend on any method on this class or instantiate
-
# new Route objects. Instead use the factory method, RouteSet#add_route
-
# to create new routes and add them to the set.
-
1
class Route
-
# Valid rack application to call if conditions are met
-
1
attr_reader :app
-
-
# A hash of conditions to match against. Conditions may be expressed
-
# as strings or regexps to match against.
-
1
attr_reader :conditions
-
-
# A hash of values that always gets merged into the parameters hash
-
1
attr_reader :defaults
-
-
# Symbol identifier for the route used with named route generations
-
1
attr_reader :name
-
-
1
attr_reader :named_captures
-
-
1
def initialize(app, conditions, defaults, name)
-
23
unless app.respond_to?(:call)
-
raise ArgumentError, 'app must be a valid rack application' \
-
' and respond to call'
-
end
-
23
@app = app
-
-
23
@name = name ? name.to_sym : nil
-
23
@defaults = (defaults || {}).freeze
-
-
23
@conditions = {}
-
-
23
conditions.each do |method, pattern|
-
44
next unless method && pattern
-
-
44
pattern = Regexp.compile("\\A#{Regexp.escape(pattern)}\\Z") if pattern.is_a?(String)
-
-
44
if pattern.is_a?(Regexp)
-
44
pattern = Utils.normalize_extended_expression(pattern)
-
44
pattern = RegexpWithNamedGroups.new(pattern)
-
44
pattern.extend(GeneratableRegexp::InstanceMethods)
-
44
pattern.defaults = @defaults
-
end
-
-
44
@conditions[method] = pattern.freeze
-
end
-
-
23
@named_captures = {}
-
23
@conditions.map { |method, condition|
-
44
next unless condition.respond_to?(:named_captures)
-
44
@named_captures[method] = Hash[condition.named_captures.map { |k, v|
-
35
[k.to_sym, v.last - 1]
-
}].freeze
-
}
-
23
@named_captures.freeze
-
-
23
@has_significant_params = @conditions.any? { |method, condition|
-
23
(condition.respond_to?(:required_params) && condition.required_params.any?) ||
-
32
(condition.respond_to?(:required_defaults) && condition.required_defaults.any?)
-
}
-
-
if @conditions.has_key?(:path_info) &&
-
23
!Utils.regexp_anchored?(@conditions[:path_info])
-
1
@prefix = true
-
1
@app = Prefix.new(@app)
-
else
-
22
@prefix = false
-
end
-
-
23
@conditions.freeze
-
end
-
-
1
def prefix?
-
@prefix
-
end
-
-
-
1
def generation_keys
-
23
@conditions.inject({}) { |keys, (_, condition)|
-
44
if condition.respond_to?(:required_defaults)
-
44
keys.merge!(condition.required_defaults)
-
else
-
keys
-
end
-
}
-
end
-
-
1
def significant_params?
-
45
@has_significant_params
-
end
-
-
1
def generate(method, params = {}, recall = {}, options = {})
-
if method.nil?
-
result = Hash[@conditions.map { |m, condition|
-
[m, condition.generate(params, recall, options)] if condition.respond_to?(:generate)
-
}]
-
return nil if result.values.compact.empty?
-
else
-
if condition = @conditions[method]
-
if condition.respond_to?(:generate)
-
result = condition.generate(params, recall, options)
-
end
-
end
-
end
-
-
if result
-
@defaults.each do |key, value|
-
params.delete(key) if params[key] == value
-
end
-
end
-
-
result
-
end
-
-
-
1
def inspect #:nodoc:
-
"#<#{self.class.name} @app=#{@app.inspect} @conditions=#{@conditions.inspect} @defaults=#{@defaults.inspect} @name=#{@name.inspect}>"
-
end
-
end
-
end
-
1
require 'rack/mount/multimap'
-
1
require 'rack/mount/route'
-
1
require 'rack/mount/utils'
-
-
1
module Rack::Mount
-
1
class RoutingError < StandardError; end
-
-
1
class RouteSet
-
# Initialize a new RouteSet without optimizations
-
1
def self.new_without_optimizations(options = {}, &block)
-
new(options.merge(:_optimize => false), &block)
-
end
-
-
# Basic RouteSet initializer.
-
#
-
# If a block is given, the set is yielded and finalized.
-
#
-
# See other aspects for other valid options:
-
# - <tt>Generation::RouteSet.new</tt>
-
# - <tt>Recognition::RouteSet.new</tt>
-
1
def initialize(options = {}, &block)
-
2
@parameters_key = options.delete(:parameters_key) || 'rack.routing_args'
-
2
@parameters_key.freeze
-
-
2
@named_routes = {}
-
-
2
@recognition_key_analyzer = Analysis::Splitting.new
-
-
2
@generation_keys = [:controller, :action]
-
2
@generation_route_keys = []
-
-
2
@request_class = options.delete(:request_class) || Rack::Request
-
447
@valid_conditions = @request_class.public_instance_methods.map! { |m| m.to_sym }
-
-
2
extend CodeGeneration unless options[:_optimize] == false
-
2
@optimized_recognize_defined = false
-
-
2
@routes = []
-
2
expire!
-
-
2
if block_given?
-
yield self
-
rehash
-
end
-
end
-
-
# Builder method to add a route to the set
-
#
-
# <tt>app</tt>:: A valid Rack app to call if the conditions are met.
-
# <tt>conditions</tt>:: A hash of conditions to match against.
-
# Conditions may be expressed as strings or
-
# regexps to match against.
-
# <tt>defaults</tt>:: A hash of values that always gets merged in
-
# <tt>name</tt>:: Symbol identifier for the route used with named
-
# route generations
-
1
def add_route(app, conditions = {}, defaults = {}, name = nil)
-
23
unless conditions.is_a?(Hash)
-
raise ArgumentError, 'conditions must be a Hash'
-
end
-
-
23
unless conditions.all? { |method, pattern|
-
44
@valid_conditions.include?(method)
-
}
-
raise ArgumentError, 'conditions may only include ' +
-
@valid_conditions.inspect
-
end
-
-
23
route = Route.new(app, conditions, defaults, name)
-
23
@routes << route
-
-
23
@recognition_key_analyzer << route.conditions
-
-
23
@named_routes[route.name] = route if route.name
-
23
@generation_route_keys << route.generation_keys
-
-
23
expire!
-
23
route
-
end
-
-
1
def recognize(obj)
-
raise 'route set not finalized' unless @recognition_graph
-
-
cache = {}
-
keys = @recognition_keys.map { |key|
-
if key.respond_to?(:call)
-
key.call(cache, obj)
-
else
-
obj.send(key)
-
end
-
}
-
-
@recognition_graph[*keys].each do |route|
-
matches = {}
-
params = route.defaults.dup
-
-
if route.conditions.all? { |method, condition|
-
value = obj.send(method)
-
if condition.is_a?(Regexp) && (m = value.match(condition))
-
matches[method] = m
-
captures = m.captures
-
route.named_captures[method].each do |k, i|
-
if v = captures[i]
-
params[k] = v
-
end
-
end
-
true
-
elsif value == condition
-
true
-
else
-
false
-
end
-
}
-
if block_given?
-
yield route, matches, params
-
else
-
return route, matches, params
-
end
-
end
-
end
-
-
nil
-
end
-
-
1
X_CASCADE = 'X-Cascade'.freeze
-
1
PASS = 'pass'.freeze
-
1
PATH_INFO = 'PATH_INFO'.freeze
-
-
# Rack compatible recognition and dispatching method. Routes are
-
# tried until one returns a non-catch status code. If no routes
-
# match, then catch status code is returned.
-
#
-
# This method can only be invoked after the RouteSet has been
-
# finalized.
-
1
def call(env)
-
raise 'route set not finalized' unless @recognition_graph
-
-
env[PATH_INFO] = Utils.normalize_path(env[PATH_INFO])
-
-
request = nil
-
req = @request_class.new(env)
-
recognize(req) do |route, matches, params|
-
# TODO: We only want to unescape params from uri related methods
-
params.each { |k, v| params[k] = Utils.unescape_uri(v) if v.is_a?(String) }
-
-
if route.prefix?
-
env[Prefix::KEY] = matches[:path_info].to_s
-
end
-
-
old_params = env[@parameters_key]
-
env[@parameters_key] = (old_params || {}).merge(params)
-
-
result = route.app.call(env)
-
-
if result[1][X_CASCADE] == PASS
-
env[@parameters_key] = old_params
-
else
-
return result
-
end
-
end
-
-
request || [404, {'Content-Type' => 'text/html', 'X-Cascade' => 'pass'}, ['Not Found']]
-
end
-
-
# Generates a url from Rack env and identifiers or significant keys.
-
#
-
# To generate a url by named route, pass the name in as a +Symbol+.
-
# url(env, :dashboard) # => "/dashboard"
-
#
-
# Additional parameters can be passed in as a hash
-
# url(env, :people, :id => "1") # => "/people/1"
-
#
-
# If no named route is given, it will fall back to a slower
-
# generation search.
-
# url(env, :controller => "people", :action => "show", :id => "1")
-
# # => "/people/1"
-
1
def url(env, *args)
-
named_route, params = nil, {}
-
-
case args.length
-
when 2
-
named_route, params = args[0], args[1].dup
-
when 1
-
if args[0].is_a?(Hash)
-
params = args[0].dup
-
else
-
named_route = args[0]
-
end
-
else
-
raise ArgumentError
-
end
-
-
only_path = params.delete(:only_path)
-
recall = env[@parameters_key] || {}
-
-
unless result = generate(:all, named_route, params, recall,
-
:parameterize => lambda { |name, param| Utils.escape_uri(param) })
-
return
-
end
-
-
parts, params = result
-
return unless parts
-
-
params.each do |k, v|
-
if v
-
params[k] = v
-
else
-
params.delete(k)
-
end
-
end
-
-
req = stubbed_request_class.new(env)
-
req._stubbed_values = parts.merge(:query_string => Utils.build_nested_query(params))
-
only_path ? req.fullpath : req.url
-
end
-
-
1
def generate(method, *args) #:nodoc:
-
raise 'route set not finalized' unless @generation_graph
-
-
method = nil if method == :all
-
named_route, params, recall, options = extract_params!(*args)
-
merged = recall.merge(params)
-
route = nil
-
-
if named_route
-
if route = @named_routes[named_route.to_sym]
-
recall = route.defaults.merge(recall)
-
url = route.generate(method, params, recall, options)
-
[url, params]
-
else
-
raise RoutingError, "#{named_route} failed to generate from #{params.inspect}"
-
end
-
else
-
keys = @generation_keys.map { |key|
-
if k = merged[key]
-
k.to_s
-
else
-
nil
-
end
-
}
-
@generation_graph[*keys].each do |r|
-
next unless r.significant_params?
-
if url = r.generate(method, params, recall, options)
-
return [url, params]
-
end
-
end
-
-
raise RoutingError, "No route matches #{params.inspect}"
-
end
-
end
-
-
# Number of routes in the set
-
1
def length
-
@routes.length
-
end
-
-
1
def rehash #:nodoc:
-
1
Utils.debug "rehashing"
-
-
1
@recognition_keys = build_recognition_keys
-
1
@recognition_graph = build_recognition_graph
-
1
@generation_graph = build_generation_graph
-
-
1
self
-
end
-
-
# Finalizes the set and builds optimized data structures. You *must*
-
# freeze the set before you can use <tt>call</tt> and <tt>url</tt>.
-
# So remember to call freeze after you are done adding routes.
-
1
def freeze
-
1
unless frozen?
-
1
rehash
-
1
stubbed_request_class
-
-
1
@recognition_key_analyzer = nil
-
1
@generation_route_keys = nil
-
1
@valid_conditions = nil
-
-
24
@routes.each { |route| route.freeze }
-
1
@routes.freeze
-
end
-
-
1
super
-
end
-
-
1
protected
-
1
def recognition_stats
-
{ :keys => @recognition_keys,
-
:keys_size => @recognition_keys.size,
-
:graph_size => @recognition_graph.size,
-
:graph_height => @recognition_graph.height,
-
:graph_average_height => @recognition_graph.average_height }
-
end
-
-
1
private
-
1
def expire! #:nodoc:
-
25
@recognition_keys = @recognition_graph = nil
-
25
@recognition_key_analyzer.expire!
-
-
25
@generation_graph = nil
-
end
-
-
# An internal helper method for constructing a nested set from
-
# the linear route set.
-
#
-
# build_nested_route_set([:request_method, :path_info]) { |route, method|
-
# route.send(method)
-
# }
-
1
def build_nested_route_set(keys, &block)
-
2
graph = Multimap.new
-
2
@routes.each_with_index do |route, index|
-
46
catch(:skip) do
-
137
k = keys.map { |key| block.call(key, index) }
-
45
Utils.pop_trailing_blanks!(k)
-
132
k.map! { |key| key || /.*/ }
-
45
graph[*k] = route
-
end
-
end
-
2
graph
-
end
-
-
1
def build_recognition_graph
-
1
build_nested_route_set(@recognition_keys) { |k, i|
-
46
@recognition_key_analyzer.possible_keys[i][k]
-
}
-
end
-
-
1
def build_recognition_keys
-
1
keys = @recognition_key_analyzer.report
-
1
Utils.debug "recognition keys - #{keys.inspect}"
-
1
keys
-
end
-
-
1
def build_generation_graph
-
1
build_nested_route_set(@generation_keys) { |k, i|
-
45
throw :skip unless @routes[i].significant_params?
-
-
44
if k = @generation_route_keys[i][k]
-
44
k.to_s
-
else
-
nil
-
end
-
}
-
end
-
-
1
def extract_params!(*args)
-
case args.length
-
when 4
-
named_route, params, recall, options = args
-
when 3
-
if args[0].is_a?(Hash)
-
params, recall, options = args
-
else
-
named_route, params, recall = args
-
end
-
when 2
-
if args[0].is_a?(Hash)
-
params, recall = args
-
else
-
named_route, params = args
-
end
-
when 1
-
if args[0].is_a?(Hash)
-
params = args[0]
-
else
-
named_route = args[0]
-
end
-
else
-
raise ArgumentError
-
end
-
-
named_route ||= nil
-
params ||= {}
-
recall ||= {}
-
options ||= {}
-
-
[named_route, params.dup, recall.dup, options.dup]
-
end
-
-
1
def stubbed_request_class
-
@stubbed_request_class ||= begin
-
1
klass = Class.new(@request_class)
-
1
klass.public_instance_methods.each do |method|
-
229
next if method =~ /^__|object_id/
-
226
klass.class_eval <<-RUBY
-
def #{method}(*args, &block)
-
@_stubbed_values[:#{method}] || super
-
end
-
RUBY
-
end
-
2
klass.class_eval { attr_accessor :_stubbed_values }
-
1
klass
-
1
end
-
end
-
end
-
end
-
1
require 'rack/mount/strexp/parser'
-
-
1
module Rack::Mount
-
1
class Strexp
-
1
class << self
-
# Parses segmented string expression and converts it into a Regexp
-
#
-
# Strexp.compile('foo')
-
# # => %r{\Afoo\Z}
-
#
-
# Strexp.compile('foo/:bar', {}, ['/'])
-
# # => %r{\Afoo/(?<bar>[^/]+)\Z}
-
#
-
# Strexp.compile(':foo.example.com')
-
# # => %r{\A(?<foo>.+)\.example\.com\Z}
-
#
-
# Strexp.compile('foo/:bar', {:bar => /[a-z]+/}, ['/'])
-
# # => %r{\Afoo/(?<bar>[a-z]+)\Z}
-
#
-
# Strexp.compile('foo(.:extension)')
-
# # => %r{\Afoo(\.(?<extension>.+))?\Z}
-
#
-
# Strexp.compile('src/*files')
-
# # => %r{\Asrc/(?<files>.+)\Z}
-
1
def compile(str, requirements = {}, separators = [], anchor = true)
-
23
return Regexp.compile(str) if str.is_a?(Regexp)
-
-
23
requirements = requirements ? requirements.dup : {}
-
23
normalize_requirements!(requirements, separators)
-
-
23
parser = StrexpParser.new
-
23
parser.anchor = anchor
-
23
parser.requirements = requirements
-
-
23
begin
-
23
re = parser.scan_str(str)
-
rescue Racc::ParseError => e
-
raise RegexpError, e.message
-
end
-
-
23
Regexp.compile(re)
-
end
-
1
alias_method :new, :compile
-
-
1
private
-
1
def normalize_requirements!(requirements, separators)
-
23
requirements.each do |key, value|
-
if value.is_a?(Regexp)
-
if regexp_has_modifiers?(value)
-
requirements[key] = value
-
else
-
requirements[key] = value.source
-
end
-
else
-
requirements[key] = Regexp.escape(value)
-
end
-
end
-
23
requirements.default ||= separators.any? ?
-
"[^#{separators.join}]+" : '.+'
-
23
requirements
-
end
-
-
1
def regexp_has_modifiers?(regexp)
-
regexp.options & (Regexp::IGNORECASE | Regexp::EXTENDED) != 0
-
end
-
end
-
end
-
end
-
#
-
# DO NOT MODIFY!!!!
-
# This file is automatically generated by Racc 1.4.6
-
# from Racc grammer file "".
-
#
-
-
1
require 'racc/parser.rb'
-
-
1
require 'rack/mount/utils'
-
1
require 'rack/mount/strexp/tokenizer'
-
-
1
module Rack
-
1
module Mount
-
1
class StrexpParser < Racc::Parser
-
-
-
1
if Regin.regexp_supports_named_captures?
-
1
REGEXP_NAMED_CAPTURE = '(?<%s>%s)'.freeze
-
else
-
REGEXP_NAMED_CAPTURE = '(?:<%s>%s)'.freeze
-
end
-
-
1
attr_accessor :anchor, :requirements
-
##### State transition tables begin ###
-
-
1
racc_action_table = [
-
1, 2, 3, 9, 4, 1, 2, 3, 12, 4,
-
1, 2, 3, 11, 4, 1, 2, 3, nil, 4 ]
-
-
1
racc_action_check = [
-
0, 0, 0, 5, 0, 3, 3, 3, 9, 3,
-
8, 8, 8, 8, 8, 6, 6, 6, nil, 6 ]
-
-
1
racc_action_pointer = [
-
-2, nil, nil, 3, nil, 3, 13, nil, 8, 8,
-
nil, nil, nil ]
-
-
1
racc_action_default = [
-
-8, -4, -5, -8, -7, -8, -1, -3, -8, -8,
-
-2, -6, 13 ]
-
-
1
racc_goto_table = [
-
6, 5, 10, 8, 10 ]
-
-
1
racc_goto_check = [
-
2, 1, 3, 2, 3 ]
-
-
1
racc_goto_pointer = [
-
nil, 1, 0, -4 ]
-
-
1
racc_goto_default = [
-
nil, nil, nil, 7 ]
-
-
1
racc_reduce_table = [
-
0, 0, :racc_error,
-
1, 8, :_reduce_1,
-
2, 9, :_reduce_2,
-
1, 9, :_reduce_none,
-
1, 10, :_reduce_4,
-
1, 10, :_reduce_5,
-
3, 10, :_reduce_6,
-
1, 10, :_reduce_7 ]
-
-
1
racc_reduce_n = 8
-
-
1
racc_shift_n = 13
-
-
1
racc_token_table = {
-
false => 0,
-
:error => 1,
-
:PARAM => 2,
-
:GLOB => 3,
-
:LPAREN => 4,
-
:RPAREN => 5,
-
:CHAR => 6 }
-
-
1
racc_nt_base = 7
-
-
1
racc_use_result_var = true
-
-
1
Racc_arg = [
-
racc_action_table,
-
racc_action_check,
-
racc_action_default,
-
racc_action_pointer,
-
racc_goto_table,
-
racc_goto_check,
-
racc_goto_default,
-
racc_goto_pointer,
-
racc_nt_base,
-
racc_reduce_table,
-
racc_token_table,
-
racc_shift_n,
-
racc_reduce_n,
-
racc_use_result_var ]
-
-
1
Racc_token_to_s_table = [
-
"$end",
-
"error",
-
"PARAM",
-
"GLOB",
-
"LPAREN",
-
"RPAREN",
-
"CHAR",
-
"$start",
-
"target",
-
"expr",
-
"token" ]
-
-
1
Racc_debug_parser = false
-
-
##### State transition tables end #####
-
-
# reduce 0 omitted
-
-
1
def _reduce_1(val, _values, result)
-
23
result = anchor ? "\\A#{val.join}\\Z" : "\\A#{val.join}"
-
23
result
-
end
-
-
1
def _reduce_2(val, _values, result)
-
299
result = val.join
-
299
result
-
end
-
-
# reduce 3 omitted
-
-
1
def _reduce_4(val, _values, result)
-
35
name = val[0].to_sym
-
35
requirement = requirements[name]
-
35
result = REGEXP_NAMED_CAPTURE % [name, requirement]
-
-
35
result
-
end
-
-
1
def _reduce_5(val, _values, result)
-
name = val[0].to_sym
-
requirement = requirements.key?(name) ? requirements[name] : '.+'
-
result = REGEXP_NAMED_CAPTURE % [name, requirement]
-
-
result
-
end
-
-
1
def _reduce_6(val, _values, result)
-
21
result = "(?:#{val[1]})?"
-
21
result
-
end
-
-
1
def _reduce_7(val, _values, result)
-
287
result = Regexp.escape(val[0])
-
287
result
-
end
-
-
1
def _reduce_none(val, _values, result)
-
val[0]
-
end
-
-
end # class StrexpParser
-
end # module Mount
-
end # module Rack
-
#--
-
# DO NOT MODIFY!!!!
-
# This file is automatically generated by rex 1.0.5.beta1
-
# from lexical definition file "lib/rack/mount/strexp/tokenizer.rex".
-
#++
-
-
1
require 'racc/parser'
-
1
class Rack::Mount::StrexpParser < Racc::Parser
-
1
require 'strscan'
-
-
1
class ScanError < StandardError ; end
-
-
1
attr_reader :lineno
-
1
attr_reader :filename
-
1
attr_accessor :state
-
-
1
def scan_setup(str)
-
23
@ss = StringScanner.new(str)
-
23
@lineno = 1
-
23
@state = nil
-
end
-
-
1
def action
-
364
yield
-
end
-
-
1
def scan_str(str)
-
23
scan_setup(str)
-
23
do_parse
-
end
-
1
alias :scan :scan_str
-
-
1
def load_file( filename )
-
@filename = filename
-
open(filename, "r") do |f|
-
scan_setup(f.read)
-
end
-
end
-
-
1
def scan_file( filename )
-
load_file(filename)
-
do_parse
-
end
-
-
-
1
def next_token
-
387
return if @ss.eos?
-
-
364
text = @ss.peek(1)
-
364
@lineno += 1 if text == "\n"
-
364
token = case @state
-
when nil
-
case
-
364
when (text = @ss.scan(/\\(\(|\)|:|\*)/))
-
action { [:CHAR, @ss[1]] }
-
-
364
when (text = @ss.scan(/\:([a-zA-Z_]\w*)/))
-
70
action { [:PARAM, @ss[1]] }
-
-
329
when (text = @ss.scan(/\*([a-zA-Z_]\w*)/))
-
action { [:GLOB, @ss[1]] }
-
-
329
when (text = @ss.scan(/\(/))
-
42
action { [:LPAREN, text] }
-
-
308
when (text = @ss.scan(/\)/))
-
42
action { [:RPAREN, text] }
-
-
287
when (text = @ss.scan(/./))
-
574
action { [:CHAR, text] }
-
-
else
-
text = @ss.string[@ss.pos .. -1]
-
raise ScanError, "can not match: '" + text + "'"
-
364
end # if
-
-
else
-
raise ScanError, "undefined state: '" + state.to_s + "'"
-
end # case state
-
364
token
-
end # def next_token
-
-
end # class
-
1
begin
-
1
require 'regin'
-
rescue LoadError
-
1
$: << File.expand_path(File.join(File.dirname(__FILE__), 'vendor/regin'))
-
1
require 'regin'
-
end
-
-
1
require 'uri'
-
-
1
module Rack::Mount
-
# Private utility methods used throughout Rack::Mount.
-
#--
-
# This module is a trash can. Try to move these functions into
-
# more appropriate contexts.
-
#++
-
1
module Utils
-
1
def silence_debug
-
1
old_debug, $DEBUG = $DEBUG, nil
-
1
yield
-
ensure
-
1
$DEBUG = old_debug
-
end
-
1
module_function :silence_debug
-
-
1
def debug(msg)
-
3
warn "Rack::Mount #{msg}" if $DEBUG
-
end
-
1
module_function :debug
-
-
# Normalizes URI path.
-
#
-
# Strips off trailing slash and ensures there is a leading slash.
-
#
-
# normalize_path("/foo") # => "/foo"
-
# normalize_path("/foo/") # => "/foo"
-
# normalize_path("foo") # => "/foo"
-
# normalize_path("") # => "/"
-
1
def normalize_path(path)
-
39
path = "/#{path}"
-
39
path.squeeze!('/')
-
39
path.sub!(%r{/+\Z}, '')
-
39
path = '/' if path == ''
-
39
path
-
end
-
1
module_function :normalize_path
-
-
# Removes trailing nils from array.
-
#
-
# pop_trailing_blanks!([1, 2, 3]) # => [1, 2, 3]
-
# pop_trailing_blanks!([1, 2, 3, nil, ""]) # => [1, 2, 3]
-
# pop_trailing_blanks!([nil]) # => []
-
# pop_trailing_blanks!([""]) # => []
-
1
def pop_trailing_blanks!(ary)
-
45
while ary.length > 0 && (ary.last.nil? || ary.last == '')
-
3
ary.pop
-
end
-
45
ary
-
end
-
1
module_function :pop_trailing_blanks!
-
-
1
RESERVED_PCHAR = ':@&=+$,;%'
-
1
SAFE_PCHAR = "#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}"
-
1
if RUBY_VERSION >= '1.9'
-
1
UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false).freeze
-
else
-
UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false, 'N').freeze
-
end
-
-
1
Parser = URI.const_defined?(:Parser) ? URI::Parser.new : URI
-
-
1
def escape_uri(uri)
-
Parser.escape(uri.to_s, UNSAFE_PCHAR)
-
end
-
1
module_function :escape_uri
-
-
1
if ''.respond_to?(:force_encoding)
-
1
def unescape_uri(uri)
-
Parser.unescape(uri).force_encoding('utf-8')
-
end
-
else
-
def unescape_uri(uri)
-
URI.unescape(uri)
-
end
-
end
-
1
module_function :unescape_uri
-
-
# Taken from Rack 1.1.x to build nested query strings
-
1
def build_nested_query(value, prefix = nil) #:nodoc:
-
case value
-
when Array
-
value.map { |v|
-
build_nested_query(v, "#{prefix}[]")
-
}.join("&")
-
when Hash
-
value.map { |k, v|
-
build_nested_query(v, prefix ? "#{prefix}[#{Rack::Utils.escape(k)}]" : Rack::Utils.escape(k))
-
}.join("&")
-
when String
-
raise ArgumentError, "value must be a Hash" if prefix.nil?
-
"#{prefix}=#{Rack::Utils.escape(value)}"
-
else
-
prefix
-
end
-
end
-
1
module_function :build_nested_query
-
-
# Determines whether the regexp must match the entire string.
-
#
-
# regexp_anchored?(/^foo$/) # => true
-
# regexp_anchored?(/foo/) # => false
-
# regexp_anchored?(/^foo/) # => false
-
# regexp_anchored?(/foo$/) # => false
-
1
def regexp_anchored?(regexp)
-
23
regexp.source =~ /\A(\\A|\^).*(\\Z|\$)\Z/m ? true : false
-
end
-
1
module_function :regexp_anchored?
-
-
1
def normalize_extended_expression(regexp)
-
44
return regexp if (regexp.options & Regexp::EXTENDED) == 0
-
source = regexp.source
-
source.gsub!(/#.+$/, '')
-
source.gsub!(/\s+/, '')
-
source.gsub!(/\\\//, '/')
-
Regexp.compile(source)
-
end
-
1
module_function :normalize_extended_expression
-
-
1
def parse_regexp(regexp)
-
88
cache = @@_parse_regexp_cache ||= {}
-
-
88
if expression = cache[regexp]
-
67
return expression
-
end
-
-
21
unless regexp.is_a?(RegexpWithNamedGroups)
-
regexp = RegexpWithNamedGroups.new(regexp)
-
end
-
-
21
expression = Regin.parse(regexp)
-
-
21
unless Regin.regexp_supports_named_captures?
-
tag_captures = Proc.new do |group|
-
case group
-
when Regin::Group
-
# TODO: dup instead of mutating
-
group.instance_variable_set('@name', regexp.names[group.index]) if group.index
-
tag_captures.call(group.expression)
-
when Regin::Expression
-
group.each { |child| tag_captures.call(child) }
-
end
-
end
-
tag_captures.call(expression)
-
end
-
-
21
cache[regexp] = expression.freeze
-
21
expression
-
rescue Racc::ParseError, Regin::Parser::ScanError
-
[]
-
end
-
1
module_function :parse_regexp
-
end
-
end
-
1
module Regin
-
1
autoload :Alternation, 'regin/alternation'
-
1
autoload :Anchor, 'regin/anchor'
-
1
autoload :Atom, 'regin/atom'
-
1
autoload :Character, 'regin/character'
-
1
autoload :CharacterClass, 'regin/character_class'
-
1
autoload :Collection, 'regin/collection'
-
1
autoload :Expression, 'regin/expression'
-
1
autoload :Group, 'regin/group'
-
1
autoload :Options, 'regin/options'
-
1
autoload :Parser, 'regin/parser'
-
-
# Detect named capture support
-
1
begin
-
1
old_debug, $DEBUG = $DEBUG, nil
-
1
eval('foo = /(?<foo>.*)/').named_captures
-
-
# Returns true if the interpreter is using the Oniguruma Regexp lib
-
# and supports named captures.
-
#
-
# /(?<foo>bar)/
-
1
def self.regexp_supports_named_captures?
-
23
true
-
end
-
rescue SyntaxError, NoMethodError
-
def self.regexp_supports_named_captures? #:nodoc:
-
false
-
end
-
ensure
-
1
$DEBUG = old_debug
-
end
-
-
-
1
POSIX_BRACKET_TYPES = %w(
-
alnum alpha ascii blank cntrl digit graph
-
lower print punct space upper word xdigit
-
foo
-
)
-
-
# Returns array of supported POSX bracket types
-
1
def self.supported_posix_bracket_types
-
14
@supported_posix_bracket_types ||= []
-
end
-
-
# Detect supported posix bracket types
-
1
begin
-
1
old_debug, $DEBUG = $DEBUG, nil
-
-
1
POSIX_BRACKET_TYPES.each do |type|
-
15
begin
-
15
eval("foo = /[[:#{type}:]]/")
-
14
supported_posix_bracket_types << type
-
rescue SyntaxError, RegexpError
-
end
-
end
-
ensure
-
1
$DEBUG = old_debug
-
end
-
-
-
# Parses Regexp and returns a Expression data structure.
-
1
def self.parse(regexp)
-
21
Parser.parse_regexp(regexp)
-
end
-
-
# Recompiles Regexp by parsing it and turning it back into a Regexp.
-
#
-
# (In the future Regin will perform some Regexp optimizations
-
# such as removing unnecessary captures and options)
-
1
def self.compile(source)
-
regexp = Regexp.compile(source)
-
expression = parse(regexp)
-
Regexp.compile(expression.to_s(true), expression.flags)
-
end
-
end
-
1
module Regin
-
1
class Anchor < Atom
-
end
-
end
-
1
module Regin
-
1
class Atom
-
1
attr_reader :value, :ignorecase
-
-
1
def initialize(value, options = {})
-
333
@value = value
-
333
@ignorecase = options[:ignorecase]
-
end
-
-
1
def option_names
-
25
%w( ignorecase )
-
end
-
-
# Returns true if expression could be treated as a literal string.
-
1
def literal?
-
false
-
end
-
-
1
def casefold?
-
ignorecase ? true : false
-
end
-
-
1
def dup(options = {})
-
25
original_options = option_names.inject({}) do |h, m|
-
75
h[m.to_sym] = send(m)
-
75
h
-
end
-
25
self.class.new(value, original_options.merge(options))
-
end
-
-
1
def to_s(parent = false)
-
84
"#{value}"
-
end
-
-
1
def inspect #:nodoc:
-
"#<#{self.class.to_s.sub('Regin::', '')} #{to_s.inspect}>"
-
end
-
-
1
def ==(other) #:nodoc:
-
84
case other
-
when String
-
84
other == to_s
-
else
-
eql?(other)
-
end
-
end
-
-
1
def eql?(other) #:nodoc:
-
other.instance_of?(self.class) &&
-
self.value.eql?(other.value) &&
-
(!!self.ignorecase).eql?(!!other.ignorecase)
-
end
-
end
-
end
-
1
module Regin
-
1
class Character < Atom
-
1
attr_reader :quantifier
-
-
1
def initialize(value, options = {})
-
292
@quantifier = options[:quantifier]
-
292
super
-
end
-
-
1
def option_names
-
25
%w( quantifier ) + super
-
end
-
-
# Returns true if expression could be treated as a literal string.
-
#
-
# A Character is literal is there is no quantifier attached to it.
-
1
def literal?
-
648
quantifier.nil? && !ignorecase
-
end
-
-
1
def to_s(parent = false)
-
290
if !parent && ignorecase
-
"(?i-mx:#{value})#{quantifier}"
-
else
-
290
"#{value}#{quantifier}"
-
end
-
end
-
-
1
def to_regexp(anchored = false)
-
re = to_s(true)
-
re = "\\A#{re}\\Z" if anchored
-
Regexp.compile(re, ignorecase)
-
end
-
-
1
def match(char)
-
to_regexp(true).match(char)
-
end
-
-
1
def include?(char)
-
533
if ignorecase
-
value.downcase == char.downcase
-
else
-
533
value == char
-
end
-
end
-
-
1
def eql?(other) #:nodoc:
-
super && quantifier.eql?(other.quantifier)
-
end
-
end
-
end
-
1
module Regin
-
1
class CharacterClass < Character
-
1
def initialize(value, options = {})
-
50
@negate = options[:negate]
-
50
super
-
end
-
-
1
def option_names
-
25
%w( negate ) + super
-
end
-
-
1
attr_reader :negate
-
-
1
def negated?
-
negate ? true : false
-
end
-
-
# Returns true if expression could be treated as a literal string.
-
#
-
# A CharacterClass is never literal.
-
1
def literal?
-
false
-
end
-
-
1
def bracketed?
-
77
value != '.' && value !~ /^\\[dDsSwW]$/
-
end
-
-
1
def to_s(parent = false)
-
77
if bracketed?
-
77
if !parent && ignorecase
-
"(?i-mx:[#{negate && '^'}#{value}])#{quantifier}"
-
else
-
77
"[#{negate && '^'}#{value}]#{quantifier}"
-
end
-
else
-
super
-
end
-
end
-
-
1
def include?(char)
-
28
re = quantifier ? to_s.sub(/#{Regexp.escape(quantifier)}$/, '') : to_s
-
28
Regexp.compile("\\A#{re}\\Z").match(char)
-
end
-
-
1
def eql?(other) #:nodoc:
-
super && negate == other.negate
-
end
-
end
-
end
-
1
module Regin
-
1
class Collection
-
1
include Enumerable
-
-
1
def initialize(*args)
-
333
@array = Array.new(*args)
-
end
-
-
1
def each
-
1874
@array.each{ |item| yield item }
-
end
-
-
1
def [](i)
-
41
@array[i]
-
end
-
-
1
def length
-
@array.length
-
end
-
1
alias_method :size, :length
-
-
1
def first
-
105
@array.first
-
end
-
-
1
def last
-
84
@array.last
-
end
-
-
1
def +(other)
-
ary = other.is_a?(self.class) ? other.internal_array : other
-
self.class.new(@array + ary)
-
end
-
-
1
def to_regexp(anchored = false)
-
35
re = to_s(true)
-
35
re = "\\A#{re}\\Z" if anchored
-
35
Regexp.compile(re, flags)
-
end
-
-
1
def match(char)
-
to_regexp.match(char)
-
end
-
-
1
def include?(char)
-
56
any? { |e| e.include?(char) }
-
end
-
-
1
def ==(other) #:nodoc:
-
case other
-
when String
-
other == to_s
-
when Array
-
other == @array
-
else
-
eql?(other)
-
end
-
end
-
-
1
def eql?(other) #:nodoc:
-
other.instance_of?(self.class) && @array.eql?(other.internal_array)
-
end
-
-
1
protected
-
1
def internal_array #:nodoc:
-
@array
-
end
-
-
1
def extract_options(args)
-
333
if args.last.is_a?(Hash)
-
225
return args[0..-2], args.last
-
else
-
108
return args, {}
-
end
-
end
-
end
-
end
-
1
module Regin
-
1
class Expression < Collection
-
1
attr_reader :ignorecase, :multiline, :extended
-
-
1
def initialize(*args)
-
333
args, options = extract_options(args)
-
-
333
@multiline = @ignorecase = @extended = nil
-
-
333
if args.length == 1 && args.first.instance_of?(Array)
-
108
super(args.first)
-
else
-
1678
args = args.map { |e| e.instance_of?(String) ? Character.new(e) : e }
-
225
super(args)
-
end
-
-
333
self.multiline = options[:multiline] if options.key?(:multiline)
-
333
self.ignorecase = options[:ignorecase] if options.key?(:ignorecase)
-
333
self.extended = options[:extended] if options.key?(:extended)
-
end
-
-
# Returns true if expression could be treated as a literal string.
-
#
-
# A Expression is literal if all its elements are literal.
-
1
def literal?
-
336
!ignorecase && all? { |e| e.literal? }
-
end
-
-
1
def anchored?
-
anchored_to_start? && anchored_to_end?
-
end
-
-
1
def anchored_to_start?
-
21
first.is_a?(Anchor) && first == '\A'
-
end
-
-
1
def anchored_to_end?
-
21
last.is_a?(Anchor) && last == '\Z'
-
end
-
-
1
def anchored_to_line?
-
21
anchored_to_start_of_line? && anchored_to_end_of_line?
-
end
-
-
1
def anchored_to_start_of_line?
-
21
anchored_to_start? || (first.is_a?(Anchor) && first == '^')
-
end
-
-
1
def anchored_to_end_of_line?
-
21
anchored_to_end? || (last.is_a?(Anchor) && last == '$')
-
end
-
-
1
def options?
-
74
options.any?(true)
-
end
-
-
1
def flags
-
35
options.to_i
-
end
-
-
1
def +(other)
-
225
ary = other.is_a?(self.class) ? other.internal_array : other
-
225
ary = @array + ary + [options.to_h(true)]
-
225
self.class.new(*ary)
-
end
-
-
1
def dup(options = {})
-
55
expression = super()
-
55
expression.multiline = options[:multiline] if options.key?(:multiline)
-
55
expression.ignorecase = options[:ignorecase] if options.key?(:ignorecase)
-
55
expression.extended = options[:extended] if options.key?(:extended)
-
55
expression
-
end
-
-
1
def to_s(parent = false)
-
95
if parent || !options?
-
434
map { |e| e.to_s(parent) }.join
-
else
-
with, without = [], []
-
multiline ? (with << 'm') : (without << 'm')
-
ignorecase ? (with << 'i') : (without << 'i')
-
extended ? (with << 'x') : (without << 'x')
-
-
with = with.join
-
without = without.any? ? "-#{without.join}" : ''
-
-
"(?#{with}#{without}:#{map { |e| e.to_s(true) }.join})"
-
end
-
end
-
-
1
def inspect #:nodoc:
-
"#<Expression #{to_s.inspect}>"
-
end
-
-
1
def casefold?
-
ignorecase
-
end
-
-
1
def eql?(other) #:nodoc:
-
super &&
-
!!self.multiline == !!other.multiline &&
-
!!self.ignorecase == !!other.ignorecase &&
-
!!self.extended == !!other.extended
-
end
-
-
1
protected
-
1
def options
-
334
Options.new(multiline, ignorecase, extended)
-
end
-
-
1
def multiline=(multiline)
-
@multiline = multiline
-
end
-
-
1
def ignorecase=(ignorecase)
-
if @ignorecase.nil?
-
@array.map! { |e| e.dup(:ignorecase => ignorecase) }
-
@ignorecase = ignorecase
-
end
-
end
-
-
1
def extended=(extended)
-
@extended = extended
-
end
-
end
-
end
-
1
module Regin
-
1
class Group
-
1
attr_reader :expression, :quantifier, :lookahead, :capture, :index, :name
-
-
1
def initialize(expression, options = {})
-
55
@quantifier = @index = @name = nil
-
55
@capture = true
-
55
@expression = expression.dup(options)
-
-
55
@quantifier = options[:quantifier] if options.key?(:quantifier)
-
55
@lookahead = options[:lookahead] if options.key?(:lookahead)
-
55
@capture = options[:capture] if options.key?(:capture)
-
55
@index = options[:index] if options.key?(:index)
-
55
@name = options[:name] if options.key?(:name)
-
end
-
-
1
def option_names
-
15
%w( quantifier capture index name )
-
end
-
-
# Returns true if expression could be treated as a literal string.
-
#
-
# A Group is literal if its expression is literal and it has no quantifier.
-
1
def literal?
-
quantifier.nil? && lookahead.nil? && expression.literal?
-
end
-
-
1
def to_s(parent = false)
-
14
if lookahead == :postive
-
"(?=#{expression.to_s(parent)})#{quantifier}"
-
14
elsif lookahead == :negative
-
"(?!#{expression.to_s(parent)})#{quantifier}"
-
14
elsif !expression.options?
-
14
"(#{capture ? '' : '?:'}#{expression.to_s(parent)})#{quantifier}"
-
elsif capture == false
-
"#{expression.to_s}#{quantifier}"
-
else
-
"(#{expression.to_s})#{quantifier}"
-
end
-
end
-
-
1
def to_regexp(anchored = false)
-
14
re = to_s
-
14
re = "\\A#{re}\\Z" if anchored
-
14
Regexp.compile(re)
-
end
-
-
1
def dup(options = {})
-
15
original_options = option_names.inject({}) do |h, m|
-
60
h[m.to_sym] = send(m)
-
60
h
-
end
-
15
self.class.new(expression, original_options.merge(options))
-
end
-
-
1
def inspect #:nodoc:
-
to_s.inspect
-
end
-
-
1
def match(char)
-
to_regexp.match(char)
-
end
-
-
1
def include?(char)
-
28
expression.include?(char)
-
end
-
-
1
def capture?
-
capture
-
end
-
-
1
def ==(other) #:nodoc:
-
case other
-
when String
-
other == to_s
-
else
-
eql?(other)
-
end
-
end
-
-
1
def eql?(other) #:nodoc:
-
other.is_a?(self.class) &&
-
self.expression == other.expression &&
-
self.quantifier == other.quantifier &&
-
self.capture == other.capture &&
-
self.index == other.index &&
-
self.name == other.name
-
end
-
end
-
end
-
1
module Regin
-
1
class Options
-
1
def self.from_int(flags)
-
21
multiline = flags & Regexp::MULTILINE != 0
-
21
ignorecase = flags & Regexp::IGNORECASE != 0
-
21
extended = flags & Regexp::EXTENDED != 0
-
-
21
new(multiline, ignorecase, extended)
-
end
-
-
1
attr_reader :multiline, :ignorecase, :extended
-
-
1
def initialize(*args)
-
355
if args.first.is_a?(Hash)
-
@multiline = args[0][:multiline]
-
@ignorecase = args[0][:ignorecase]
-
@extended = args[0][:extended]
-
else
-
355
@multiline = args[0]
-
355
@ignorecase = args[1]
-
355
@extended = args[2]
-
end
-
end
-
-
1
def any?(explicit = false)
-
95
if explicit
-
74
!multiline.nil? || !ignorecase.nil? || !extended.nil?
-
else
-
21
multiline || ignorecase || extended
-
end
-
end
-
-
1
def to_h(explicit = false)
-
246
if explicit
-
225
options = {}
-
225
options[:multiline] = multiline unless multiline.nil?
-
225
options[:ignorecase] = ignorecase unless ignorecase.nil?
-
225
options[:extended] = extended unless extended.nil?
-
225
options
-
else
-
{ :multiline => multiline,
-
:ignorecase => ignorecase,
-
21
:extended => extended }
-
end
-
end
-
-
1
def to_i
-
35
flag = 0
-
35
flag |= Regexp::MULTILINE if multiline
-
35
flag |= Regexp::IGNORECASE if ignorecase
-
35
flag |= Regexp::EXTENDED if extended
-
35
flag
-
end
-
end
-
end
-
#
-
# DO NOT MODIFY!!!!
-
# This file is automatically generated by Racc 1.4.6
-
# from Racc grammer file "".
-
#
-
-
1
require 'racc/parser.rb'
-
1
module Regin
-
1
class Parser < Racc::Parser #:nodoc: all
-
-
1
def self.parse_regexp(regexp)
-
21
options = Options.from_int(regexp.options)
-
-
21
parser = new
-
21
parser.options_stack << options.to_h
-
-
21
expression = parser.scan_str(regexp.source)
-
21
expression = expression.dup(options.to_h) if options.any?
-
21
expression
-
end
-
-
1
attr_accessor :options_stack
-
-
1
def initialize
-
21
@capture_index = 0
-
21
@capture_index_stack = []
-
21
@options_stack = []
-
end
-
-
##### State transition tables begin ###
-
-
1
racc_action_table = [
-
43, 44, 46, 48, 4, 52, 17, 9, 10, 11,
-
13, 30, 28, 19, 20, 21, 4, 6, 7, 9,
-
10, 11, 13, 55, 45, 47, 49, 50, 4, 6,
-
7, 9, 10, 11, 13, 80, 56, 47, 49, 50,
-
4, 6, 7, 9, 10, 11, 13, 83, 47, 49,
-
50, 81, 4, 6, 7, 9, 10, 11, 13, 74,
-
47, 49, 50, 31, 4, 6, 7, 9, 10, 11,
-
13, 47, 49, 50, 32, 85, 4, 6, 7, 9,
-
10, 11, 13, 47, 49, 50, 86, 33, 4, 6,
-
7, 9, 10, 11, 13, 47, 49, 50, 88, 89,
-
4, 6, 7, 9, 10, 11, 13, 47, 49, 50,
-
27, 91, 4, 6, 7, 9, 10, 11, 13, 22,
-
34, 23, 36, 41, 25, 6, 7, 58, 60, 62,
-
63, 64, 65, 66, 67, 68, 69, 70, 57, 59,
-
61, 22, 22, 38, 22, 72, 25, 39, 95, 39,
-
47, 49, 50 ]
-
-
1
racc_action_check = [
-
28, 28, 28, 28, 13, 29, 3, 13, 13, 13,
-
13, 14, 13, 3, 3, 3, 31, 13, 13, 31,
-
31, 31, 31, 35, 28, 28, 28, 28, 0, 31,
-
31, 0, 0, 0, 0, 51, 35, 84, 84, 84,
-
80, 0, 0, 80, 80, 80, 80, 73, 73, 73,
-
73, 71, 1, 80, 80, 1, 1, 1, 1, 42,
-
42, 42, 42, 15, 48, 1, 1, 48, 48, 48,
-
48, 83, 83, 83, 19, 75, 46, 48, 48, 46,
-
46, 46, 46, 87, 87, 87, 76, 20, 30, 46,
-
46, 30, 30, 30, 30, 77, 77, 77, 78, 79,
-
43, 30, 30, 43, 43, 43, 43, 74, 74, 74,
-
12, 81, 44, 43, 43, 44, 44, 44, 44, 4,
-
21, 4, 22, 27, 4, 44, 44, 36, 36, 36,
-
36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
-
36, 23, 24, 24, 37, 37, 23, 24, 90, 37,
-
45, 45, 45 ]
-
-
1
racc_action_pointer = [
-
25, 49, nil, -5, 116, nil, nil, nil, nil, nil,
-
nil, nil, 110, 1, 9, 61, nil, nil, nil, 63,
-
76, 112, 108, 138, 139, nil, nil, 123, -12, -5,
-
85, 13, nil, nil, nil, 15, 105, 141, nil, nil,
-
nil, nil, 23, 97, 109, 113, 73, nil, 61, nil,
-
nil, 21, nil, nil, nil, nil, nil, nil, nil, nil,
-
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
-
nil, 37, nil, 11, 70, 65, 76, 58, 88, 89,
-
37, 107, nil, 34, 0, nil, nil, 46, nil, nil,
-
138, nil, nil, nil, nil, nil ]
-
-
1
racc_action_default = [
-
-59, -5, -7, -9, -59, -10, -23, -24, -15, -13,
-
-14, -16, -59, -59, -1, -2, -6, -27, -8, -25,
-
-26, -59, -59, -59, -59, -36, -35, -59, -59, -59,
-
-59, -59, -28, -29, -32, -59, -59, -59, -11, -34,
-
-33, 96, -59, -59, -59, -59, -59, -56, -59, -57,
-
-58, -59, -17, -3, -4, -31, -30, -49, -38, -50,
-
-39, -51, -40, -41, -42, -43, -44, -45, -46, -47,
-
-48, -59, -12, -59, -59, -59, -59, -59, -59, -59,
-
-59, -59, -55, -59, -59, -18, -19, -59, -21, -22,
-
-59, -37, -54, -53, -52, -20 ]
-
-
1
racc_goto_table = [
-
12, 24, 40, 35, 42, 53, 54, 16, 18, 71,
-
51, nil, nil, 29, nil, 40, nil, nil, 73, nil,
-
37, 77, nil, nil, nil, nil, nil, nil, nil, nil,
-
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
-
nil, nil, nil, 75, 76, nil, 78, nil, 79, 82,
-
84, nil, nil, 87, nil, nil, nil, nil, nil, 92,
-
93, nil, nil, 94, nil, nil, nil, nil, nil, nil,
-
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
-
90 ]
-
-
1
racc_goto_check = [
-
1, 9, 13, 12, 15, 3, 3, 5, 7, 14,
-
11, nil, nil, 1, nil, 13, nil, nil, 15, nil,
-
9, 15, nil, nil, nil, nil, nil, nil, nil, nil,
-
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
-
nil, nil, nil, 1, 1, nil, 1, nil, 1, 15,
-
15, nil, nil, 15, nil, nil, nil, nil, nil, 15,
-
15, nil, nil, 15, nil, nil, nil, nil, nil, nil,
-
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
-
1 ]
-
-
1
racc_goto_pointer = [
-
nil, 0, nil, -25, nil, 6, nil, 5, nil, -3,
-
nil, -18, -18, -22, -27, -24 ]
-
-
1
racc_goto_default = [
-
nil, nil, 14, 15, 1, 2, 3, nil, 5, nil,
-
8, nil, nil, 26, nil, nil ]
-
-
1
racc_reduce_table = [
-
0, 0, :racc_error,
-
1, 41, :_reduce_1,
-
1, 41, :_reduce_none,
-
3, 42, :_reduce_3,
-
3, 42, :_reduce_4,
-
1, 43, :_reduce_5,
-
2, 44, :_reduce_6,
-
1, 44, :_reduce_7,
-
2, 45, :_reduce_8,
-
1, 45, :_reduce_none,
-
1, 46, :_reduce_none,
-
3, 46, :_reduce_11,
-
4, 46, :_reduce_12,
-
1, 46, :_reduce_13,
-
1, 46, :_reduce_14,
-
1, 46, :_reduce_15,
-
1, 46, :_reduce_16,
-
3, 48, :_reduce_17,
-
5, 48, :_reduce_18,
-
5, 48, :_reduce_19,
-
6, 48, :_reduce_20,
-
5, 48, :_reduce_21,
-
5, 48, :_reduce_22,
-
1, 50, :_reduce_none,
-
1, 50, :_reduce_none,
-
1, 47, :_reduce_none,
-
1, 47, :_reduce_none,
-
1, 47, :_reduce_none,
-
2, 47, :_reduce_28,
-
2, 47, :_reduce_29,
-
3, 47, :_reduce_30,
-
2, 52, :_reduce_31,
-
1, 52, :_reduce_none,
-
2, 49, :_reduce_33,
-
2, 49, :_reduce_34,
-
1, 49, :_reduce_none,
-
1, 49, :_reduce_none,
-
5, 53, :_reduce_37,
-
1, 54, :_reduce_none,
-
1, 54, :_reduce_none,
-
1, 54, :_reduce_none,
-
1, 54, :_reduce_none,
-
1, 54, :_reduce_none,
-
1, 54, :_reduce_none,
-
1, 54, :_reduce_none,
-
1, 54, :_reduce_none,
-
1, 54, :_reduce_none,
-
1, 54, :_reduce_none,
-
1, 54, :_reduce_none,
-
1, 54, :_reduce_none,
-
1, 54, :_reduce_none,
-
1, 54, :_reduce_none,
-
4, 51, :_reduce_52,
-
4, 51, :_reduce_53,
-
4, 51, :_reduce_54,
-
3, 51, :_reduce_55,
-
1, 55, :_reduce_56,
-
1, 55, :_reduce_57,
-
1, 55, :_reduce_58 ]
-
-
1
racc_reduce_n = 59
-
-
1
racc_shift_n = 96
-
-
1
racc_token_table = {
-
false => 0,
-
:error => 1,
-
:BAR => 2,
-
:LBRACK => 3,
-
:RBRACK => 4,
-
:NEGATE => 5,
-
:CCLASS => 6,
-
:DOT => 7,
-
:CHAR => 8,
-
:LPAREN => 9,
-
:RPAREN => 10,
-
:QMARK => 11,
-
:EQUAL => 12,
-
:BANG => 13,
-
:COLON => 14,
-
:NAME => 15,
-
:L_ANCHOR => 16,
-
:R_ANCHOR => 17,
-
:STAR => 18,
-
:PLUS => 19,
-
:LCURLY => 20,
-
:RCURLY => 21,
-
"alnum" => 22,
-
"alpha" => 23,
-
"ascii" => 24,
-
"blank" => 25,
-
"cntrl" => 26,
-
"digit" => 27,
-
"graph" => 28,
-
"lower" => 29,
-
"print" => 30,
-
"punct" => 31,
-
"space" => 32,
-
"upper" => 33,
-
"word" => 34,
-
"xdigit" => 35,
-
:MINUS => 36,
-
:MULTILINE => 37,
-
:IGNORECASE => 38,
-
:EXTENDED => 39 }
-
-
1
racc_nt_base = 40
-
-
1
racc_use_result_var = true
-
-
1
Racc_arg = [
-
racc_action_table,
-
racc_action_check,
-
racc_action_default,
-
racc_action_pointer,
-
racc_goto_table,
-
racc_goto_check,
-
racc_goto_default,
-
racc_goto_pointer,
-
racc_nt_base,
-
racc_reduce_table,
-
racc_token_table,
-
racc_shift_n,
-
racc_reduce_n,
-
racc_use_result_var ]
-
-
1
Racc_token_to_s_table = [
-
"$end",
-
"error",
-
"BAR",
-
"LBRACK",
-
"RBRACK",
-
"NEGATE",
-
"CCLASS",
-
"DOT",
-
"CHAR",
-
"LPAREN",
-
"RPAREN",
-
"QMARK",
-
"EQUAL",
-
"BANG",
-
"COLON",
-
"NAME",
-
"L_ANCHOR",
-
"R_ANCHOR",
-
"STAR",
-
"PLUS",
-
"LCURLY",
-
"RCURLY",
-
"\"alnum\"",
-
"\"alpha\"",
-
"\"ascii\"",
-
"\"blank\"",
-
"\"cntrl\"",
-
"\"digit\"",
-
"\"graph\"",
-
"\"lower\"",
-
"\"print\"",
-
"\"punct\"",
-
"\"space\"",
-
"\"upper\"",
-
"\"word\"",
-
"\"xdigit\"",
-
"MINUS",
-
"MULTILINE",
-
"IGNORECASE",
-
"EXTENDED",
-
"$start",
-
"expression",
-
"alternation",
-
"subexpression",
-
"expression_ary",
-
"quantified_atom",
-
"atom",
-
"quantifier",
-
"group",
-
"bracket_expression",
-
"anchor",
-
"options",
-
"quantifier_char",
-
"posix_bracket_expression",
-
"posix_bracket_type",
-
"modifier" ]
-
-
1
Racc_debug_parser = false
-
-
##### State transition tables end #####
-
-
# reduce 0 omitted
-
-
1
def _reduce_1(val, _values, result)
-
result = Expression.new(val[0])
-
result
-
end
-
-
# reduce 2 omitted
-
-
1
def _reduce_3(val, _values, result)
-
result = val[0] + [val[2]]
-
result
-
end
-
-
1
def _reduce_4(val, _values, result)
-
result = Alternation.new(val[0], val[2])
-
result
-
end
-
-
1
def _reduce_5(val, _values, result)
-
61
result = Expression.new(val[0])
-
61
result
-
end
-
-
1
def _reduce_6(val, _values, result)
-
287
result = val[0] + [val[1]]
-
287
result
-
end
-
-
1
def _reduce_7(val, _values, result)
-
61
result = [val[0]]
-
61
result
-
end
-
-
1
def _reduce_8(val, _values, result)
-
40
result = val[0].dup(:quantifier => val[1])
-
40
result
-
end
-
-
# reduce 9 omitted
-
-
# reduce 10 omitted
-
-
1
def _reduce_11(val, _values, result)
-
result = CharacterClass.new(val[1])
-
result
-
end
-
-
1
def _reduce_12(val, _values, result)
-
25
result = CharacterClass.new(val[2], :negate => true)
-
25
result
-
end
-
-
1
def _reduce_13(val, _values, result)
-
result = CharacterClass.new(val[0])
-
result
-
end
-
-
1
def _reduce_14(val, _values, result)
-
result = CharacterClass.new('.')
-
result
-
end
-
-
1
def _reduce_15(val, _values, result)
-
41
result = Anchor.new(val[0])
-
41
result
-
end
-
-
1
def _reduce_16(val, _values, result)
-
242
result = Character.new(val[0])
-
242
result
-
end
-
-
1
def _reduce_17(val, _values, result)
-
result = Group.new(val[1], :index => @capture_index_stack.pop)
-
-
result
-
end
-
-
1
def _reduce_18(val, _values, result)
-
result = Group.new(val[3], :index => @capture_index_stack.pop, :lookahead => :postive)
-
-
result
-
end
-
-
1
def _reduce_19(val, _values, result)
-
result = Group.new(val[3], :index => @capture_index_stack.pop, :lookahead => :negative)
-
-
result
-
end
-
-
1
def _reduce_20(val, _values, result)
-
result = Group.new(val[4], val[2].merge(:capture => false))
-
@options_stack.pop
-
-
result
-
end
-
-
1
def _reduce_21(val, _values, result)
-
15
result = Group.new(val[3], :capture => false);
-
-
15
result
-
end
-
-
1
def _reduce_22(val, _values, result)
-
25
result = Group.new(val[3], :name => val[2], :index => @capture_index_stack.pop);
-
-
25
result
-
end
-
-
# reduce 23 omitted
-
-
# reduce 24 omitted
-
-
# reduce 25 omitted
-
-
# reduce 26 omitted
-
-
# reduce 27 omitted
-
-
1
def _reduce_28(val, _values, result)
-
result = val.join
-
result
-
end
-
-
1
def _reduce_29(val, _values, result)
-
result = val.join
-
result
-
end
-
-
1
def _reduce_30(val, _values, result)
-
result = val.join
-
result
-
end
-
-
1
def _reduce_31(val, _values, result)
-
result = val.join
-
result
-
end
-
-
# reduce 32 omitted
-
-
1
def _reduce_33(val, _values, result)
-
result = val.join
-
result
-
end
-
-
1
def _reduce_34(val, _values, result)
-
50
result = val.join
-
50
result
-
end
-
-
# reduce 35 omitted
-
-
# reduce 36 omitted
-
-
1
def _reduce_37(val, _values, result)
-
result = val.join
-
result
-
end
-
-
# reduce 38 omitted
-
-
# reduce 39 omitted
-
-
# reduce 40 omitted
-
-
# reduce 41 omitted
-
-
# reduce 42 omitted
-
-
# reduce 43 omitted
-
-
# reduce 44 omitted
-
-
# reduce 45 omitted
-
-
# reduce 46 omitted
-
-
# reduce 47 omitted
-
-
# reduce 48 omitted
-
-
# reduce 49 omitted
-
-
# reduce 50 omitted
-
-
# reduce 51 omitted
-
-
1
def _reduce_52(val, _values, result)
-
@options_stack << result = { val[1] => false, val[2] => false, val[3] => false }
-
-
result
-
end
-
-
1
def _reduce_53(val, _values, result)
-
@options_stack << result = { val[0] => true, val[2] => false, val[3] => false }
-
-
result
-
end
-
-
1
def _reduce_54(val, _values, result)
-
@options_stack << result = { val[0] => true, val[1] => true, val[3] => false }
-
-
result
-
end
-
-
1
def _reduce_55(val, _values, result)
-
@options_stack << result = { val[0] => true, val[1] => true, val[2] => true }
-
-
result
-
end
-
-
1
def _reduce_56(val, _values, result)
-
result = :multiline
-
result
-
end
-
-
1
def _reduce_57(val, _values, result)
-
result = :ignorecase
-
result
-
end
-
-
1
def _reduce_58(val, _values, result)
-
result = :extended
-
result
-
end
-
-
1
def _reduce_none(val, _values, result)
-
val[0]
-
end
-
-
end # class Parser
-
end # module Regin
-
-
1
require 'regin/tokenizer'
-
#--
-
# DO NOT MODIFY!!!!
-
# This file is automatically generated by rex 1.0.5
-
# from lexical definition file "lib/regin/tokenizer.rex".
-
#++
-
-
1
require 'racc/parser'
-
1
class Regin::Parser < Racc::Parser
-
1
require 'strscan'
-
-
1
class ScanError < StandardError ; end
-
-
1
attr_reader :lineno
-
1
attr_reader :filename
-
1
attr_accessor :state
-
-
1
def scan_setup(str)
-
21
@ss = StringScanner.new(str)
-
21
@lineno = 1
-
21
@state = nil
-
end
-
-
1
def action
-
633
yield
-
end
-
-
1
def scan_str(str)
-
21
scan_setup(str)
-
21
do_parse
-
end
-
1
alias :scan :scan_str
-
-
1
def load_file( filename )
-
@filename = filename
-
open(filename, "r") do |f|
-
scan_setup(f.read)
-
end
-
end
-
-
1
def scan_file( filename )
-
load_file(filename)
-
do_parse
-
end
-
-
-
1
def next_token
-
654
return if @ss.eos?
-
-
# skips empty actions
-
633
until token = _next_token or @ss.eos?; end
-
633
token
-
end
-
-
1
def _next_token
-
633
text = @ss.peek(1)
-
633
@lineno += 1 if text == "\n"
-
633
token = case @state
-
when nil
-
case
-
453
when (text = @ss.scan(/\\[dDsSwW]/))
-
action { [:CCLASS, text] }
-
-
453
when (text = @ss.scan(/\^|\\A/))
-
42
action { [:L_ANCHOR, text] }
-
-
432
when (text = @ss.scan(/\$|\\Z/))
-
40
action { [:R_ANCHOR, text] }
-
-
412
when (text = @ss.scan(/<(\w+)>/))
-
50
action { [:NAME, @ss[1]] }
-
-
387
when (text = @ss.scan(/\(/))
-
40
action {
-
40
@capture_index_stack << @capture_index
-
40
@capture_index += 1
-
40
@state = :OPTIONS if @ss.peek(1) == '?';
-
40
[:LPAREN, text]
-
}
-
-
-
347
when (text = @ss.scan(/\)/))
-
80
action { [:RPAREN, text] }
-
-
307
when (text = @ss.scan(/\[/))
-
50
action { @state = :CCLASS; [:LBRACK, text] }
-
-
282
when (text = @ss.scan(/\{/))
-
action { [:LCURLY, text] }
-
-
282
when (text = @ss.scan(/\}/))
-
action { [:RCURLY, text] }
-
-
282
when (text = @ss.scan(/\|/))
-
action { [:BAR, text] }
-
-
282
when (text = @ss.scan(/\./))
-
action { [:DOT, text] }
-
-
282
when (text = @ss.scan(/\!/))
-
action { [:BANG, text] }
-
-
282
when (text = @ss.scan(/\=/))
-
action { [:EQUAL, text] }
-
-
282
when (text = @ss.scan(/\?/))
-
30
action { [:QMARK, text] }
-
-
267
when (text = @ss.scan(/\+/))
-
50
action { [:PLUS, text] }
-
-
242
when (text = @ss.scan(/\*/))
-
action { [:STAR, text] }
-
-
242
when (text = @ss.scan(/\#/))
-
action {
-
if @options_stack[-1][:extended]
-
@state = :COMMENT;
-
next_token
-
else
-
[:CHAR, text]
-
end
-
}
-
-
-
242
when (text = @ss.scan(/\s|\n/))
-
action {
-
if @options_stack[-1][:extended]
-
next_token
-
else
-
[:CHAR, text]
-
end
-
}
-
-
-
242
when (text = @ss.scan(/\\(.)/))
-
30
action { [:CHAR, @ss[1]] }
-
-
227
when (text = @ss.scan(/./))
-
454
action { [:CHAR, text] }
-
-
else
-
text = @ss.string[@ss.pos .. -1]
-
raise ScanError, "can not match: '" + text + "'"
-
453
end # if
-
-
when :CCLASS
-
case
-
125
when (text = @ss.scan(/\[/))
-
action { [:LBRACK, text] }
-
-
125
when (text = @ss.scan(/\]/))
-
50
action { @state = nil; [:RBRACK, text] }
-
-
100
when (text = @ss.scan(/\^/))
-
50
action { [@ss.string[@ss.pos-2, 1] == '[' ? :NEGATE : :CHAR, text] }
-
-
75
when (text = @ss.scan(/:/))
-
action {
-
if @ss.string[@ss.pos-2, 1] == '['
-
@state = :POSIX_CCLASS
-
[:COLON, text]
-
else
-
[:CHAR, text]
-
end
-
}
-
-
-
75
when (text = @ss.scan(/\\-/))
-
action { [:CHAR, text] }
-
-
75
when (text = @ss.scan(/\\[dDsSwW]/))
-
action { [:CHAR, text] }
-
-
75
when (text = @ss.scan(/\\(.)/))
-
action { [:CHAR, @ss[1]] }
-
-
75
when (text = @ss.scan(/./))
-
150
action { [:CHAR, text] }
-
-
else
-
text = @ss.string[@ss.pos .. -1]
-
raise ScanError, "can not match: '" + text + "'"
-
125
end # if
-
-
when :POSIX_CCLASS
-
case
-
when (text = @ss.scan(/\w+/))
-
action { [text, text] }
-
-
when (text = @ss.scan(/:/))
-
action { [:COLON, text] }
-
-
when (text = @ss.scan(/\]/))
-
action { @state = :CCLASS; [:RBRACK, text] }
-
-
else
-
text = @ss.string[@ss.pos .. -1]
-
raise ScanError, "can not match: '" + text + "'"
-
end # if
-
-
when :OPTIONS
-
case
-
55
when (text = @ss.scan(/\?/))
-
40
action {
-
40
@state = nil unless @ss.peek(1) =~ /-|m|i|x|:/
-
40
[:QMARK, text]
-
}
-
-
-
15
when (text = @ss.scan(/\-/))
-
action { [:MINUS, text] }
-
-
15
when (text = @ss.scan(/m/))
-
action { [:MULTILINE, text] }
-
-
15
when (text = @ss.scan(/i/))
-
action { [:IGNORECASE, text] }
-
-
15
when (text = @ss.scan(/x/))
-
action { [:EXTENDED, text] }
-
-
15
when (text = @ss.scan(/\:/))
-
15
action {
-
15
@capture_index_stack.pop
-
15
@capture_index -= 1
-
15
@state = nil;
-
15
[:COLON, text]
-
}
-
-
-
else
-
text = @ss.string[@ss.pos .. -1]
-
raise ScanError, "can not match: '" + text + "'"
-
55
end # if
-
-
when :COMMENT
-
case
-
when (text = @ss.scan(/\n/))
-
action { @state = nil; next_token }
-
-
when (text = @ss.scan(/./))
-
action { next_token }
-
-
else
-
text = @ss.string[@ss.pos .. -1]
-
raise ScanError, "can not match: '" + text + "'"
-
end # if
-
-
else
-
raise ScanError, "undefined state: '" + state.to_s + "'"
-
end # case state
-
633
token
-
end # def _next_token
-
-
end # class
-
1
module Rack
-
-
1
class MockSession # :nodoc:
-
1
attr_writer :cookie_jar
-
1
attr_reader :default_host
-
-
1
def initialize(app, default_host = Rack::Test::DEFAULT_HOST)
-
@app = app
-
@after_request = []
-
@default_host = default_host
-
@last_request = nil
-
@last_response = nil
-
end
-
-
1
def after_request(&block)
-
@after_request << block
-
end
-
-
1
def clear_cookies
-
@cookie_jar = Rack::Test::CookieJar.new([], @default_host)
-
end
-
-
1
def set_cookie(cookie, uri = nil)
-
cookie_jar.merge(cookie, uri)
-
end
-
-
1
def request(uri, env)
-
env["HTTP_COOKIE"] ||= cookie_jar.for(uri)
-
@last_request = Rack::Request.new(env)
-
status, headers, body = @app.call(@last_request.env)
-
-
@last_response = MockResponse.new(status, headers, body, env["rack.errors"].flush)
-
body.close if body.respond_to?(:close)
-
-
cookie_jar.merge(last_response.headers["Set-Cookie"], uri)
-
-
@after_request.each { |hook| hook.call }
-
-
if @last_response.respond_to?(:finish)
-
@last_response.finish
-
else
-
@last_response
-
end
-
end
-
-
# Return the last request issued in the session. Raises an error if no
-
# requests have been sent yet.
-
1
def last_request
-
raise Rack::Test::Error.new("No request yet. Request a page first.") unless @last_request
-
@last_request
-
end
-
-
# Return the last response received in the session. Raises an error if
-
# no requests have been sent yet.
-
1
def last_response
-
raise Rack::Test::Error.new("No response yet. Request a page first.") unless @last_response
-
@last_response
-
end
-
-
1
def cookie_jar
-
@cookie_jar ||= Rack::Test::CookieJar.new([], @default_host)
-
end
-
-
end
-
-
end
-
1
require "uri"
-
1
require "rack"
-
1
require "rack/mock_session"
-
1
require "rack/test/cookie_jar"
-
1
require "rack/test/mock_digest_request"
-
1
require "rack/test/utils"
-
1
require "rack/test/methods"
-
1
require "rack/test/uploaded_file"
-
-
1
module Rack
-
1
module Test
-
1
VERSION = "0.6.1"
-
-
1
DEFAULT_HOST = "example.org"
-
1
MULTIPART_BOUNDARY = "----------XnJLe9ZIbbGUYtzPQJ16u1"
-
-
# The common base class for exceptions raised by Rack::Test
-
1
class Error < StandardError; end
-
-
# This class represents a series of requests issued to a Rack app, sharing
-
# a single cookie jar
-
#
-
# Rack::Test::Session's methods are most often called through Rack::Test::Methods,
-
# which will automatically build a session when it's first used.
-
1
class Session
-
1
extend Forwardable
-
1
include Rack::Test::Utils
-
-
1
def_delegators :@rack_mock_session, :clear_cookies, :set_cookie, :last_response, :last_request
-
-
# Creates a Rack::Test::Session for a given Rack app or Rack::MockSession.
-
#
-
# Note: Generally, you won't need to initialize a Rack::Test::Session directly.
-
# Instead, you should include Rack::Test::Methods into your testing context.
-
# (See README.rdoc for an example)
-
1
def initialize(mock_session)
-
@headers = {}
-
-
if mock_session.is_a?(MockSession)
-
@rack_mock_session = mock_session
-
else
-
@rack_mock_session = MockSession.new(mock_session)
-
end
-
-
@default_host = @rack_mock_session.default_host
-
end
-
-
# Issue a GET request for the given URI with the given params and Rack
-
# environment. Stores the issues request object in #last_request and
-
# the app's response in #last_response. Yield #last_response to a block
-
# if given.
-
#
-
# Example:
-
# get "/"
-
1
def get(uri, params = {}, env = {}, &block)
-
env = env_for(uri, env.merge(:method => "GET", :params => params))
-
process_request(uri, env, &block)
-
end
-
-
# Issue a POST request for the given URI. See #get
-
#
-
# Example:
-
# post "/signup", "name" => "Bryan"
-
1
def post(uri, params = {}, env = {}, &block)
-
env = env_for(uri, env.merge(:method => "POST", :params => params))
-
process_request(uri, env, &block)
-
end
-
-
# Issue a PUT request for the given URI. See #get
-
#
-
# Example:
-
# put "/"
-
1
def put(uri, params = {}, env = {}, &block)
-
env = env_for(uri, env.merge(:method => "PUT", :params => params))
-
process_request(uri, env, &block)
-
end
-
-
# Issue a DELETE request for the given URI. See #get
-
#
-
# Example:
-
# delete "/"
-
1
def delete(uri, params = {}, env = {}, &block)
-
env = env_for(uri, env.merge(:method => "DELETE", :params => params))
-
process_request(uri, env, &block)
-
end
-
-
# Issue an OPTIONS request for the given URI. See #get
-
#
-
# Example:
-
# options "/"
-
1
def options(uri, params = {}, env = {}, &block)
-
env = env_for(uri, env.merge(:method => "OPTIONS", :params => params))
-
process_request(uri, env, &block)
-
end
-
-
# Issue a HEAD request for the given URI. See #get
-
#
-
# Example:
-
# head "/"
-
1
def head(uri, params = {}, env = {}, &block)
-
env = env_for(uri, env.merge(:method => "HEAD", :params => params))
-
process_request(uri, env, &block)
-
end
-
-
# Issue a request to the Rack app for the given URI and optional Rack
-
# environment. Stores the issues request object in #last_request and
-
# the app's response in #last_response. Yield #last_response to a block
-
# if given.
-
#
-
# Example:
-
# request "/"
-
1
def request(uri, env = {}, &block)
-
env = env_for(uri, env)
-
process_request(uri, env, &block)
-
end
-
-
# Set a header to be included on all subsequent requests through the
-
# session. Use a value of nil to remove a previously configured header.
-
#
-
# In accordance with the Rack spec, headers will be included in the Rack
-
# environment hash in HTTP_USER_AGENT form.
-
#
-
# Example:
-
# header "User-Agent", "Firefox"
-
1
def header(name, value)
-
if value.nil?
-
@headers.delete(name)
-
else
-
@headers[name] = value
-
end
-
end
-
-
# Set the username and password for HTTP Basic authorization, to be
-
# included in subsequent requests in the HTTP_AUTHORIZATION header.
-
#
-
# Example:
-
# basic_authorize "bryan", "secret"
-
1
def basic_authorize(username, password)
-
encoded_login = ["#{username}:#{password}"].pack("m*")
-
header('Authorization', "Basic #{encoded_login}")
-
end
-
-
1
alias_method :authorize, :basic_authorize
-
-
# Set the username and password for HTTP Digest authorization, to be
-
# included in subsequent requests in the HTTP_AUTHORIZATION header.
-
#
-
# Example:
-
# digest_authorize "bryan", "secret"
-
1
def digest_authorize(username, password)
-
@digest_username = username
-
@digest_password = password
-
end
-
-
# Rack::Test will not follow any redirects automatically. This method
-
# will follow the redirect returned (including setting the Referer header
-
# on the new request) in the last response. If the last response was not
-
# a redirect, an error will be raised.
-
1
def follow_redirect!
-
unless last_response.redirect?
-
raise Error.new("Last response was not a redirect. Cannot follow_redirect!")
-
end
-
-
get(last_response["Location"], {}, { "HTTP_REFERER" => last_request.url })
-
end
-
-
1
private
-
-
1
def env_for(path, env)
-
uri = URI.parse(path)
-
uri.path = "/#{uri.path}" unless uri.path[0] == ?/
-
uri.host ||= @default_host
-
-
env = default_env.merge(env)
-
-
env["HTTP_HOST"] ||= [uri.host, uri.port].compact.join(":")
-
-
env.update("HTTPS" => "on") if URI::HTTPS === uri
-
env["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest" if env[:xhr]
-
-
# TODO: Remove this after Rack 1.1 has been released.
-
# Stringifying and upcasing methods has be commit upstream
-
env["REQUEST_METHOD"] ||= env[:method] ? env[:method].to_s.upcase : "GET"
-
-
if env["REQUEST_METHOD"] == "GET"
-
params = env[:params] || {}
-
params = parse_nested_query(params) if params.is_a?(String)
-
params.update(parse_nested_query(uri.query))
-
uri.query = build_nested_query(params)
-
elsif !env.has_key?(:input)
-
env["CONTENT_TYPE"] ||= "application/x-www-form-urlencoded"
-
-
if env[:params].is_a?(Hash)
-
if data = build_multipart(env[:params])
-
env[:input] = data
-
env["CONTENT_LENGTH"] ||= data.length.to_s
-
env["CONTENT_TYPE"] = "multipart/form-data; boundary=#{MULTIPART_BOUNDARY}"
-
else
-
env[:input] = params_to_string(env[:params])
-
end
-
else
-
env[:input] = env[:params]
-
end
-
end
-
-
env.delete(:params)
-
-
if env.has_key?(:cookie)
-
set_cookie(env.delete(:cookie), uri)
-
end
-
-
Rack::MockRequest.env_for(uri.to_s, env)
-
end
-
-
1
def process_request(uri, env)
-
uri = URI.parse(uri)
-
uri.host ||= @default_host
-
-
@rack_mock_session.request(uri, env)
-
-
if retry_with_digest_auth?(env)
-
auth_env = env.merge({
-
"HTTP_AUTHORIZATION" => digest_auth_header,
-
"rack-test.digest_auth_retry" => true
-
})
-
auth_env.delete('rack.request')
-
process_request(uri.path, auth_env)
-
else
-
yield last_response if block_given?
-
-
last_response
-
end
-
end
-
-
1
def digest_auth_header
-
challenge = last_response["WWW-Authenticate"].split(" ", 2).last
-
params = Rack::Auth::Digest::Params.parse(challenge)
-
-
params.merge!({
-
"username" => @digest_username,
-
"nc" => "00000001",
-
"cnonce" => "nonsensenonce",
-
"uri" => last_request.path_info,
-
"method" => last_request.env["REQUEST_METHOD"],
-
})
-
-
params["response"] = MockDigestRequest.new(params).response(@digest_password)
-
-
"Digest #{params}"
-
end
-
-
1
def retry_with_digest_auth?(env)
-
last_response.status == 401 &&
-
digest_auth_configured? &&
-
!env["rack-test.digest_auth_retry"]
-
end
-
-
1
def digest_auth_configured?
-
@digest_username
-
end
-
-
1
def default_env
-
{ "rack.test" => true, "REMOTE_ADDR" => "127.0.0.1" }.merge(headers_for_env)
-
end
-
-
1
def headers_for_env
-
converted_headers = {}
-
-
@headers.each do |name, value|
-
env_key = name.upcase.gsub("-", "_")
-
env_key = "HTTP_" + env_key unless "CONTENT_TYPE" == env_key
-
converted_headers[env_key] = value
-
end
-
-
converted_headers
-
end
-
-
1
def params_to_string(params)
-
case params
-
when Hash then build_nested_query(params)
-
when nil then ""
-
else params
-
end
-
end
-
-
end
-
-
1
def self.encoding_aware_strings?
-
defined?(Encoding) && "".respond_to?(:encode)
-
end
-
-
end
-
end
-
1
require "uri"
-
1
require "time"
-
-
1
module Rack
-
1
module Test
-
-
1
class Cookie # :nodoc:
-
1
include Rack::Utils
-
-
# :api: private
-
1
attr_reader :name, :value
-
-
# :api: private
-
1
def initialize(raw, uri = nil, default_host = DEFAULT_HOST)
-
@default_host = default_host
-
uri ||= default_uri
-
-
# separate the name / value pair from the cookie options
-
@name_value_raw, options = raw.split(/[;,] */n, 2)
-
-
@name, @value = parse_query(@name_value_raw, ';').to_a.first
-
@options = parse_query(options, ';')
-
-
@options["domain"] ||= (uri.host || default_host)
-
@options["path"] ||= uri.path.sub(/\/[^\/]*\Z/, "")
-
end
-
-
1
def replaces?(other)
-
[name.downcase, domain, path] == [other.name.downcase, other.domain, other.path]
-
end
-
-
# :api: private
-
1
def raw
-
@name_value_raw
-
end
-
-
# :api: private
-
1
def empty?
-
@value.nil? || @value.empty?
-
end
-
-
# :api: private
-
1
def domain
-
@options["domain"]
-
end
-
-
1
def secure?
-
@options.has_key?("secure")
-
end
-
-
# :api: private
-
1
def path
-
@options["path"].strip || "/"
-
end
-
-
# :api: private
-
1
def expires
-
Time.parse(@options["expires"]) if @options["expires"]
-
end
-
-
# :api: private
-
1
def expired?
-
expires && expires < Time.now
-
end
-
-
# :api: private
-
1
def valid?(uri)
-
uri ||= default_uri
-
-
if uri.host.nil?
-
uri.host = @default_host
-
end
-
-
real_domain = domain =~ /^\./ ? domain[1..-1] : domain
-
(!secure? || (secure? && uri.scheme == "https")) &&
-
uri.host =~ Regexp.new("#{Regexp.escape(real_domain)}$", Regexp::IGNORECASE) &&
-
uri.path =~ Regexp.new("^#{Regexp.escape(path)}")
-
end
-
-
# :api: private
-
1
def matches?(uri)
-
! expired? && valid?(uri)
-
end
-
-
# :api: private
-
1
def <=>(other)
-
# Orders the cookies from least specific to most
-
[name, path, domain.reverse] <=> [other.name, other.path, other.domain.reverse]
-
end
-
-
1
protected
-
-
1
def default_uri
-
URI.parse("//" + @default_host + "/")
-
end
-
-
end
-
-
1
class CookieJar # :nodoc:
-
-
# :api: private
-
1
def initialize(cookies = [], default_host = DEFAULT_HOST)
-
@default_host = default_host
-
@cookies = cookies
-
@cookies.sort!
-
end
-
-
1
def [](name)
-
cookies = hash_for(nil)
-
# TODO: Should be case insensitive
-
cookies[name] && cookies[name].value
-
end
-
-
1
def []=(name, value)
-
merge("#{name}=#{Rack::Utils.escape(value)}")
-
end
-
-
1
def merge(raw_cookies, uri = nil)
-
return unless raw_cookies
-
-
if raw_cookies.is_a? String
-
raw_cookies = raw_cookies.split("\n")
-
raw_cookies.reject!{|c| c.empty? }
-
end
-
-
raw_cookies.each do |raw_cookie|
-
cookie = Cookie.new(raw_cookie, uri, @default_host)
-
self << cookie if cookie.valid?(uri)
-
end
-
end
-
-
1
def <<(new_cookie)
-
@cookies.reject! do |existing_cookie|
-
new_cookie.replaces?(existing_cookie)
-
end
-
-
@cookies << new_cookie
-
@cookies.sort!
-
end
-
-
# :api: private
-
1
def for(uri)
-
hash_for(uri).values.map { |c| c.raw }.join(';')
-
end
-
-
1
def to_hash
-
cookies = {}
-
-
hash_for(nil).each do |name, cookie|
-
cookies[name] = cookie.value
-
end
-
-
return cookies
-
end
-
-
1
protected
-
-
1
def hash_for(uri = nil)
-
cookies = {}
-
-
# The cookies are sorted by most specific first. So, we loop through
-
# all the cookies in order and add it to a hash by cookie name if
-
# the cookie can be sent to the current URI. It's added to the hash
-
# so that when we are done, the cookies will be unique by name and
-
# we'll have grabbed the most specific to the URI.
-
@cookies.each do |cookie|
-
cookies[cookie.name] = cookie if !uri || cookie.matches?(uri)
-
end
-
-
return cookies
-
end
-
-
end
-
-
end
-
end
-
1
require "forwardable"
-
-
1
module Rack
-
1
module Test
-
-
# This module serves as the primary integration point for using Rack::Test
-
# in a testing environment. It depends on an app method being defined in the
-
# same context, and provides the Rack::Test API methods (see Rack::Test::Session
-
# for their documentation).
-
#
-
# Example:
-
#
-
# class HomepageTest < Test::Unit::TestCase
-
# include Rack::Test::Methods
-
#
-
# def app
-
# MyApp.new
-
# end
-
# end
-
1
module Methods
-
1
extend Forwardable
-
-
1
def rack_mock_session(name = :default) # :nodoc:
-
return build_rack_mock_session unless name
-
-
@_rack_mock_sessions ||= {}
-
@_rack_mock_sessions[name] ||= build_rack_mock_session
-
end
-
-
1
def build_rack_mock_session # :nodoc:
-
Rack::MockSession.new(app)
-
end
-
-
1
def rack_test_session(name = :default) # :nodoc:
-
return build_rack_test_session(name) unless name
-
-
@_rack_test_sessions ||= {}
-
@_rack_test_sessions[name] ||= build_rack_test_session(name)
-
end
-
-
1
def build_rack_test_session(name) # :nodoc:
-
Rack::Test::Session.new(rack_mock_session(name))
-
end
-
-
1
def current_session # :nodoc:
-
rack_test_session(_current_session_names.last)
-
end
-
-
1
def with_session(name) # :nodoc:
-
_current_session_names.push(name)
-
yield rack_test_session(name)
-
_current_session_names.pop
-
end
-
-
1
def _current_session_names # :nodoc:
-
@_current_session_names ||= [:default]
-
end
-
-
1
METHODS = [
-
:request,
-
:get,
-
:post,
-
:put,
-
:delete,
-
:options,
-
:head,
-
:follow_redirect!,
-
:header,
-
:set_cookie,
-
:clear_cookies,
-
:authorize,
-
:basic_authorize,
-
:digest_authorize,
-
:last_response,
-
:last_request
-
]
-
-
1
def_delegators :current_session, *METHODS
-
end
-
end
-
end
-
1
module Rack
-
1
module Test
-
-
1
class MockDigestRequest # :nodoc:
-
-
1
def initialize(params)
-
@params = params
-
end
-
-
1
def method_missing(sym)
-
if @params.has_key? k = sym.to_s
-
return @params[k]
-
end
-
-
super
-
end
-
-
1
def method
-
@params['method']
-
end
-
-
1
def response(password)
-
Rack::Auth::Digest::MD5.new(nil).send :digest, self, password
-
end
-
-
end
-
-
end
-
end
-
1
require "tempfile"
-
1
require "fileutils"
-
-
1
module Rack
-
1
module Test
-
-
# Wraps a Tempfile with a content type. Including one or more UploadedFile's
-
# in the params causes Rack::Test to build and issue a multipart request.
-
#
-
# Example:
-
# post "/photos", "file" => Rack::Test::UploadedFile.new("me.jpg", "image/jpeg")
-
1
class UploadedFile
-
-
# The filename, *not* including the path, of the "uploaded" file
-
1
attr_reader :original_filename
-
-
# The content type of the "uploaded" file
-
1
attr_accessor :content_type
-
-
1
def initialize(path, content_type = "text/plain", binary = false)
-
raise "#{path} file does not exist" unless ::File.exist?(path)
-
-
@content_type = content_type
-
@original_filename = ::File.basename(path)
-
-
@tempfile = Tempfile.new(@original_filename)
-
@tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
-
@tempfile.binmode if binary
-
-
FileUtils.copy_file(path, @tempfile.path)
-
end
-
-
1
def path
-
@tempfile.path
-
end
-
-
1
alias_method :local_path, :path
-
-
1
def method_missing(method_name, *args, &block) #:nodoc:
-
@tempfile.__send__(method_name, *args, &block)
-
end
-
-
1
def respond_to?(method_name, include_private = false) #:nodoc:
-
@tempfile.respond_to?(method_name, include_private) || super
-
end
-
-
end
-
-
end
-
end
-
1
module Rack
-
1
module Test
-
-
1
module Utils # :nodoc:
-
1
include Rack::Utils
-
-
1
def build_nested_query(value, prefix = nil)
-
case value
-
when Array
-
value.map do |v|
-
unless unescape(prefix) =~ /\[\]$/
-
prefix = "#{prefix}[]"
-
end
-
build_nested_query(v, "#{prefix}")
-
end.join("&")
-
when Hash
-
value.map do |k, v|
-
build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
-
end.join("&")
-
when NilClass
-
prefix.to_s
-
else
-
"#{prefix}=#{escape(value)}"
-
end
-
end
-
-
1
module_function :build_nested_query
-
-
1
def build_multipart(params, first = true)
-
if first
-
unless params.is_a?(Hash)
-
raise ArgumentError, "value must be a Hash"
-
end
-
-
multipart = false
-
query = lambda { |value|
-
case value
-
when Array
-
value.each(&query)
-
when Hash
-
value.values.each(&query)
-
when UploadedFile
-
multipart = true
-
end
-
}
-
params.values.each(&query)
-
return nil unless multipart
-
end
-
-
flattened_params = Hash.new
-
-
params.each do |key, value|
-
k = first ? key.to_s : "[#{key}]"
-
-
case value
-
when Array
-
value.map do |v|
-
-
if (v.is_a?(Hash))
-
build_multipart(v, false).each { |subkey, subvalue|
-
flattened_params["#{k}[]#{subkey}"] = subvalue
-
}
-
else
-
flattened_params["#{k}[]"] = value
-
end
-
-
end
-
when Hash
-
build_multipart(value, false).each { |subkey, subvalue|
-
flattened_params[k + subkey] = subvalue
-
}
-
else
-
flattened_params[k] = value
-
end
-
end
-
-
if first
-
build_parts(flattened_params)
-
else
-
flattened_params
-
end
-
end
-
-
1
module_function :build_multipart
-
-
1
private
-
1
def build_parts(parameters)
-
parameters.map { |name, value|
-
if value.respond_to?(:original_filename)
-
build_file_part(name, value)
-
-
elsif value.is_a?(Array) and value.all? { |v| v.respond_to?(:original_filename) }
-
value.map do |v|
-
build_file_part(name, v)
-
end.join
-
-
else
-
primitive_part = build_primitive_part(name, value)
-
Rack::Test.encoding_aware_strings? ? primitive_part.force_encoding('BINARY') : primitive_part
-
end
-
-
}.join + "--#{MULTIPART_BOUNDARY}--\r"
-
end
-
-
1
def build_primitive_part(parameter_name, value)
-
unless value.is_a? Array
-
value = [value]
-
end
-
value.map do |v|
-
<<-EOF
-
--#{MULTIPART_BOUNDARY}\r
-
Content-Disposition: form-data; name="#{parameter_name}"\r
-
\r
-
#{v}\r
-
EOF
-
end.join
-
end
-
-
1
def build_file_part(parameter_name, uploaded_file)
-
::File.open(uploaded_file.path, "rb") do |physical_file|
-
physical_file.set_encoding(Encoding::BINARY) if physical_file.respond_to?(:set_encoding)
-
<<-EOF
-
--#{MULTIPART_BOUNDARY}\r
-
Content-Disposition: form-data; name="#{parameter_name}"; filename="#{escape(uploaded_file.original_filename)}"\r
-
Content-Type: #{uploaded_file.content_type}\r
-
Content-Length: #{::File.stat(uploaded_file.path).size}\r
-
\r
-
#{physical_file.read}\r
-
EOF
-
end
-
end
-
-
end
-
-
end
-
end
-
1
require "rails"
-
-
%w(
-
active_record
-
1
action_controller
-
action_mailer
-
active_resource
-
rails/test_unit
-
sprockets
-
).each do |framework|
-
6
begin
-
6
require "#{framework}/railtie"
-
rescue LoadError
-
end
-
end
-
1
require "active_support/notifications"
-
1
require "active_support/dependencies"
-
1
require "active_support/descendants_tracker"
-
-
1
module Rails
-
1
class Application
-
1
module Bootstrap
-
1
include Initializable
-
-
1
initializer :load_environment_hook do end
-
-
1
initializer :load_active_support do
-
1
require "active_support/all" unless config.active_support.bare
-
end
-
-
# Preload all frameworks specified by the Configuration#frameworks.
-
# Used by Passenger to ensure everything's loaded before forking and
-
# to avoid autoload race conditions in JRuby.
-
1
initializer :preload_frameworks do
-
1
ActiveSupport::Autoload.eager_autoload! if config.preload_frameworks
-
end
-
-
# Initialize the logger early in the stack in case we need to log some deprecation.
-
1
initializer :initialize_logger do
-
1
Rails.logger ||= config.logger || begin
-
1
path = config.paths["log"].first
-
1
logger = ActiveSupport::BufferedLogger.new(path)
-
1
logger.level = ActiveSupport::BufferedLogger.const_get(config.log_level.to_s.upcase)
-
1
logger.auto_flushing = false if Rails.env.production?
-
1
logger
-
rescue StandardError => e
-
logger = ActiveSupport::BufferedLogger.new(STDERR)
-
logger.level = ActiveSupport::BufferedLogger::WARN
-
logger.warn(
-
"Rails Error: Unable to access log file. Please ensure that #{path} exists and is chmod 0666. " +
-
"The log level has been raised to WARN and the output directed to STDERR until the problem is fixed."
-
)
-
logger
-
end
-
2
at_exit { Rails.logger.flush if Rails.logger.respond_to?(:flush) }
-
end
-
-
# Initialize cache early in the stack so railties can make use of it.
-
1
initializer :initialize_cache do
-
1
unless defined?(RAILS_CACHE)
-
2
silence_warnings { Object.const_set "RAILS_CACHE", ActiveSupport::Cache.lookup_store(config.cache_store) }
-
-
1
if RAILS_CACHE.respond_to?(:middleware)
-
1
config.middleware.insert_before("Rack::Runtime", RAILS_CACHE.middleware)
-
end
-
end
-
end
-
-
1
initializer :set_clear_dependencies_hook do
-
1
ActionDispatch::Reloader.to_cleanup do
-
ActiveSupport::DescendantsTracker.clear
-
ActiveSupport::Dependencies.clear
-
end
-
end
-
-
# Sets the dependency loading mechanism.
-
# TODO: Remove files from the $" and always use require.
-
1
initializer :initialize_dependency_mechanism do
-
1
ActiveSupport::Dependencies.mechanism = config.cache_classes ? :require : :load
-
end
-
-
1
initializer :bootstrap_hook do |app|
-
1
ActiveSupport.run_load_hooks(:before_initialize, app)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/string/encoding'
-
1
require 'rails/engine/configuration'
-
-
1
module Rails
-
1
class Application
-
1
class Configuration < ::Rails::Engine::Configuration
-
1
attr_accessor :allow_concurrency, :asset_host, :asset_path, :assets,
-
:cache_classes, :cache_store, :consider_all_requests_local,
-
:dependency_loading, :encoding, :filter_parameters,
-
:force_ssl, :helpers_paths, :logger, :preload_frameworks,
-
:reload_plugins, :secret_token, :serve_static_assets,
-
:static_cache_control, :session_options, :time_zone, :whiny_nils
-
-
1
attr_writer :log_level
-
-
1
def initialize(*)
-
1
super
-
1
self.encoding = "utf-8"
-
1
@allow_concurrency = false
-
1
@consider_all_requests_local = false
-
1
@filter_parameters = []
-
1
@helpers_paths = []
-
1
@dependency_loading = true
-
1
@serve_static_assets = true
-
1
@static_cache_control = nil
-
1
@force_ssl = false
-
1
@session_store = :cookie_store
-
1
@session_options = {}
-
1
@time_zone = "UTC"
-
1
@log_level = nil
-
1
@middleware = app_middleware
-
1
@generators = app_generators
-
1
@cache_store = [ :file_store, "#{root}/tmp/cache/" ]
-
-
1
@assets = ActiveSupport::OrderedOptions.new
-
1
@assets.enabled = false
-
1
@assets.paths = []
-
1
@assets.precompile = [ /\w+\.(?!js|css).+/, /application.(css|js)$/ ]
-
1
@assets.prefix = "/assets"
-
1
@assets.version = ''
-
1
@assets.debug = false
-
1
@assets.compile = true
-
1
@assets.digest = false
-
1
@assets.manifest = "#{root}/public#{@assets.prefix}"
-
1
@assets.cache_store = [ :file_store, "#{root}/tmp/cache/assets/" ]
-
1
@assets.js_compressor = nil
-
1
@assets.css_compressor = nil
-
end
-
-
1
def compiled_asset_path
-
"/"
-
end
-
-
1
def encoding=(value)
-
2
@encoding = value
-
2
if "ruby".encoding_aware?
-
2
Encoding.default_external = value
-
2
Encoding.default_internal = value
-
else
-
$KCODE = value
-
if $KCODE == "NONE"
-
raise "The value you specified for config.encoding is " \
-
"invalid. The possible values are UTF8, SJIS, or EUC"
-
end
-
end
-
end
-
-
1
def paths
-
@paths ||= begin
-
1
paths = super
-
1
paths.add "config/database", :with => "config/database.yml"
-
1
paths.add "config/environment", :with => "config/environment.rb"
-
1
paths.add "lib/templates"
-
1
paths.add "log", :with => "log/#{Rails.env}.log"
-
1
paths.add "public"
-
1
paths.add "public/javascripts"
-
1
paths.add "public/stylesheets"
-
1
paths.add "tmp"
-
1
paths
-
20
end
-
end
-
-
# Enable threaded mode. Allows concurrent requests to controller actions and
-
# multiple database connections. Also disables automatic dependency loading
-
# after boot, and disables reloading code on every request, as these are
-
# fundamentally incompatible with thread safety.
-
1
def threadsafe!
-
self.preload_frameworks = true
-
self.cache_classes = true
-
self.dependency_loading = false
-
self.allow_concurrency = true
-
self
-
end
-
-
# Loads and returns the contents of the #database_configuration_file. The
-
# contents of the file are processed via ERB before being sent through
-
# YAML::load.
-
1
def database_configuration
-
1
require 'erb'
-
1
YAML::load(ERB.new(IO.read(paths["config/database"].first)).result)
-
end
-
-
1
def log_level
-
1
@log_level ||= Rails.env.production? ? :info : :debug
-
end
-
-
1
def colorize_logging
-
@colorize_logging
-
end
-
-
1
def colorize_logging=(val)
-
@colorize_logging = val
-
ActiveSupport::LogSubscriber.colorize_logging = val
-
self.generators.colorize_logging = val
-
end
-
-
1
def session_store(*args)
-
3
if args.empty?
-
2
case @session_store
-
when :disabled
-
nil
-
when :active_record_store
-
ActiveRecord::SessionStore
-
when Symbol
-
2
ActionDispatch::Session.const_get(@session_store.to_s.camelize)
-
else
-
@session_store
-
end
-
else
-
1
@session_store = args.shift
-
1
@session_options = args.shift || {}
-
end
-
end
-
end
-
end
-
end
-
1
module Rails
-
1
class Application
-
1
module Finisher
-
1
include Initializable
-
-
1
initializer :add_generator_templates do
-
1
config.generators.templates.unshift(*paths["lib/templates"].existent)
-
end
-
-
1
initializer :ensure_autoload_once_paths_as_subset do
-
1
extra = ActiveSupport::Dependencies.autoload_once_paths -
-
ActiveSupport::Dependencies.autoload_paths
-
-
1
unless extra.empty?
-
abort <<-end_error
-
autoload_once_paths must be a subset of the autoload_paths.
-
Extra items in autoload_once_paths: #{extra * ','}
-
end_error
-
end
-
end
-
-
1
initializer :add_to_prepare_blocks do
-
1
config.to_prepare_blocks.each do |block|
-
ActionDispatch::Reloader.to_prepare(&block)
-
end
-
end
-
-
1
initializer :add_builtin_route do |app|
-
1
if Rails.env.development?
-
app.routes.append do
-
match '/rails/info/properties' => "rails/info#properties"
-
end
-
end
-
end
-
-
1
initializer :build_middleware_stack do
-
1
build_middleware_stack
-
end
-
-
1
initializer :run_prepare_callbacks do
-
1
ActionDispatch::Reloader.prepare!
-
end
-
-
1
initializer :define_main_app_helper do |app|
-
1
app.routes.define_mounted_helper(:main_app)
-
end
-
-
1
initializer :eager_load! do
-
1
if config.cache_classes && !$rails_rake_task
-
1
ActiveSupport.run_load_hooks(:before_eager_load, self)
-
1
eager_load!
-
end
-
end
-
-
1
initializer :finisher_hook do
-
1
ActiveSupport.run_load_hooks(:after_initialize, self)
-
end
-
-
# Force routes to be loaded just at the end and add it to to_prepare callbacks
-
# This needs to be after the finisher hook to ensure routes added in the hook
-
# are still loaded.
-
1
initializer :set_routes_reloader do |app|
-
2
reloader = lambda { app.routes_reloader.execute_if_updated }
-
1
reloader.call
-
1
ActionDispatch::Reloader.to_prepare(&reloader)
-
end
-
-
# Disable dependency loading during request cycle
-
1
initializer :disable_dependency_loading do
-
1
if config.cache_classes && !config.dependency_loading
-
ActiveSupport::Dependencies.unhook!
-
end
-
end
-
end
-
end
-
end
-
1
require 'rails/engine/railties'
-
-
1
module Rails
-
1
class Application < Engine
-
1
class Railties < Rails::Engine::Railties
-
1
def all(&block)
-
2
@all ||= railties + engines + plugins
-
2
@all.each(&block) if block
-
2
@all
-
end
-
end
-
end
-
end
-
1
module Rails
-
1
class Application
-
1
class RoutesReloader < ::ActiveSupport::FileUpdateChecker
-
1
attr_reader :route_sets
-
-
1
def initialize
-
2
super([]) { reload! }
-
1
@route_sets = []
-
end
-
-
1
def reload!
-
1
clear!
-
1
load_paths
-
1
finalize!
-
ensure
-
1
revert
-
end
-
-
1
protected
-
-
1
def clear!
-
1
route_sets.each do |routes|
-
1
routes.disable_clear_and_finalize = true
-
1
routes.clear!
-
end
-
end
-
-
1
def load_paths
-
2
paths.each { |path| load(path) }
-
end
-
-
1
def finalize!
-
1
route_sets.each do |routes|
-
2
ActiveSupport.on_load(:action_controller) { routes.finalize! }
-
end
-
end
-
-
1
def revert
-
1
route_sets.each do |routes|
-
1
routes.disable_clear_and_finalize = false
-
end
-
end
-
end
-
end
-
end
-
1
require 'rails/railtie/configuration'
-
-
1
module Rails
-
1
class Engine
-
1
class Configuration < ::Rails::Railtie::Configuration
-
1
attr_reader :root
-
1
attr_writer :middleware, :eager_load_paths, :autoload_once_paths, :autoload_paths
-
1
attr_accessor :plugins
-
-
1
def initialize(root=nil)
-
3
super()
-
3
@root = root
-
3
@generators = app_generators.dup
-
end
-
-
# Returns the middleware stack for the engine.
-
1
def middleware
-
3
@middleware ||= Rails::Configuration::MiddlewareStackProxy.new
-
end
-
-
# Holds generators configuration:
-
#
-
# config.generators do |g|
-
# g.orm :datamapper, :migration => true
-
# g.template_engine :haml
-
# g.test_framework :rspec
-
# end
-
#
-
# If you want to disable color in console, do:
-
#
-
# config.generators.colorize_logging = false
-
#
-
1
def generators #:nodoc
-
2
@generators ||= Rails::Configuration::Generators.new
-
2
yield(@generators) if block_given?
-
2
@generators
-
end
-
-
1
def paths
-
@paths ||= begin
-
3
paths = Rails::Paths::Root.new(@root)
-
3
paths.add "app", :eager_load => true, :glob => "*"
-
3
paths.add "app/assets", :glob => "*"
-
3
paths.add "app/controllers", :eager_load => true
-
3
paths.add "app/helpers", :eager_load => true
-
3
paths.add "app/models", :eager_load => true
-
3
paths.add "app/mailers", :eager_load => true
-
3
paths.add "app/views"
-
3
paths.add "lib", :load_path => true
-
3
paths.add "lib/assets", :glob => "*"
-
3
paths.add "lib/tasks", :glob => "**/*.rake"
-
3
paths.add "config"
-
3
paths.add "config/environments", :glob => "#{Rails.env}.rb"
-
3
paths.add "config/initializers", :glob => "**/*.rb"
-
3
paths.add "config/locales", :glob => "*.{rb,yml}"
-
3
paths.add "config/routes", :with => "config/routes.rb"
-
3
paths.add "db"
-
3
paths.add "db/migrate"
-
3
paths.add "db/seeds", :with => "db/seeds.rb"
-
3
paths.add "vendor", :load_path => true
-
3
paths.add "vendor/assets", :glob => "*"
-
3
paths.add "vendor/plugins"
-
3
paths
-
29
end
-
end
-
-
1
def root=(value)
-
@root = paths.path = Pathname.new(value).expand_path
-
end
-
-
1
def eager_load_paths
-
9
@eager_load_paths ||= paths.eager_load
-
end
-
-
1
def autoload_once_paths
-
9
@autoload_once_paths ||= paths.autoload_once
-
end
-
-
1
def autoload_paths
-
6
@autoload_paths ||= paths.autoload_paths
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/time/conversions'
-
-
1
module Rails
-
1
module Rack
-
# Log the request started and flush all loggers after it.
-
1
class Logger < ActiveSupport::LogSubscriber
-
1
def initialize(app)
-
1
@app = app
-
end
-
-
1
def call(env)
-
before_dispatch(env)
-
@app.call(env)
-
ensure
-
after_dispatch(env)
-
end
-
-
1
protected
-
-
1
def before_dispatch(env)
-
request = ActionDispatch::Request.new(env)
-
path = request.filtered_path
-
-
info "\n\nStarted #{request.request_method} \"#{path}\" " \
-
"for #{request.ip} at #{Time.now.to_default_s}"
-
end
-
-
1
def after_dispatch(env)
-
ActiveSupport::LogSubscriber.flush_all!
-
end
-
end
-
end
-
end
-
# Make double-sure the RAILS_ENV is not set to production,
-
# so fixtures aren't loaded into that environment
-
1
abort("Abort testing: Your Rails environment is running in production mode!") if Rails.env.production?
-
-
1
require 'test/unit'
-
1
require 'active_support/core_ext/kernel/requires'
-
1
require 'active_support/test_case'
-
1
require 'action_controller/test_case'
-
1
require 'action_dispatch/testing/integration'
-
-
1
if defined?(Test::Unit::Util::BacktraceFilter) && ENV['BACKTRACE'].nil?
-
require 'rails/backtrace_cleaner'
-
Test::Unit::Util::BacktraceFilter.module_eval { include Rails::BacktraceFilterForTestUnit }
-
end
-
-
1
if defined?(MiniTest)
-
# Enable turn if it is available
-
1
begin
-
1
require 'turn'
-
-
if MiniTest::Unit.respond_to?(:use_natural_language_case_names=)
-
MiniTest::Unit.use_natural_language_case_names = true
-
end
-
rescue LoadError
-
end
-
end
-
-
1
if defined?(ActiveRecord)
-
1
require 'active_record/test_case'
-
-
1
class ActiveSupport::TestCase
-
1
include ActiveRecord::TestFixtures
-
1
self.fixture_path = "#{Rails.root}/test/fixtures/"
-
-
1
setup do
-
ActiveRecord::IdentityMap.clear
-
end
-
end
-
-
1
ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path
-
-
1
def create_fixtures(*table_names, &block)
-
Fixtures.create_fixtures(ActiveSupport::TestCase.fixture_path, table_names, {}, &block)
-
end
-
end
-
-
1
class ActionController::TestCase
-
1
setup do
-
@routes = Rails.application.routes
-
end
-
end
-
-
1
class ActionDispatch::IntegrationTest
-
1
setup do
-
@routes = Rails.application.routes
-
end
-
end
-
1
module Rails
-
1
class TestUnitRailtie < Rails::Railtie
-
1
config.app_generators do |c|
-
1
c.test_framework :test_unit, :fixture => true,
-
:fixture_replacement => nil
-
-
1
c.integration_tool :test_unit
-
1
c.performance_tool :test_unit
-
end
-
-
1
rake_tasks do
-
load "rails/test_unit/testing.rake"
-
end
-
end
-
end
-
1
module RSpec
-
1
module Rails
-
1
class Railtie < ::Rails::Railtie
-
# Rails-3.0.1 requires config.app_generators instead of 3.0.0's config.generators
-
1
generators = config.respond_to?(:app_generators) ? config.app_generators : config.generators
-
1
generators.integration_tool :rspec
-
1
generators.test_framework :rspec
-
-
1
rake_tasks do
-
load "rspec/rails/tasks/rspec.rake"
-
end
-
end
-
end
-
end
-
1
dir = File.dirname(__FILE__)
-
1
$LOAD_PATH.unshift dir unless $LOAD_PATH.include?(dir)
-
-
# This is necessary to set so that the Haml code that tries to load Sass
-
# knows that Sass is indeed loading,
-
# even if there's some crazy autoload stuff going on.
-
1
SASS_BEGUN_TO_LOAD = true unless defined?(SASS_BEGUN_TO_LOAD)
-
-
1
require 'sass/version'
-
-
# The module that contains everything Sass-related:
-
#
-
# * {Sass::Engine} is the class used to render Sass/SCSS within Ruby code.
-
# * {Sass::Plugin} is interfaces with web frameworks (Rails and Merb in particular).
-
# * {Sass::SyntaxError} is raised when Sass encounters an error.
-
# * {Sass::CSS} handles conversion of CSS to Sass.
-
#
-
# Also see the {file:SASS_REFERENCE.md full Sass reference}.
-
1
module Sass
-
# Compile a Sass or SCSS string to CSS.
-
# Defaults to SCSS.
-
#
-
# @param contents [String] The contents of the Sass file.
-
# @param options [{Symbol => Object}] An options hash;
-
# see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
-
# @raise [Sass::SyntaxError] if there's an error in the document
-
# @raise [Encoding::UndefinedConversionError] if the source encoding
-
# cannot be converted to UTF-8
-
# @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
-
1
def self.compile(contents, options = {})
-
options[:syntax] ||= :scss
-
Engine.new(contents, options).to_css
-
end
-
-
# Compile a file on disk to CSS.
-
#
-
# @param filename [String] The path to the Sass, SCSS, or CSS file on disk.
-
# @param options [{Symbol => Object}] An options hash;
-
# see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
-
# @raise [Sass::SyntaxError] if there's an error in the document
-
# @raise [Encoding::UndefinedConversionError] if the source encoding
-
# cannot be converted to UTF-8
-
# @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
-
#
-
# @overload compile_file(filename, options = {})
-
# Return the compiled CSS rather than writing it to a file.
-
#
-
# @return [String] The compiled CSS.
-
#
-
# @overload compile_file(filename, css_filename, options = {})
-
# Write the compiled CSS to a file.
-
#
-
# @param css_filename [String] The location to which to write the compiled CSS.
-
1
def self.compile_file(filename, *args)
-
options = args.last.is_a?(Hash) ? args.pop : {}
-
css_filename = args.shift
-
result = Sass::Engine.for_file(filename, options).render
-
if css_filename
-
options[:css_filename] ||= css_filename
-
open(css_filename,"w") {|css_file| css_file.write(result)}
-
nil
-
else
-
result
-
end
-
end
-
end
-
-
1
require 'sass/logger'
-
1
require 'sass/util'
-
-
1
require 'sass/engine'
-
1
require 'sass/plugin' if defined?(Merb::Plugins)
-
1
require 'sass/railtie'
-
1
require 'stringio'
-
-
1
module Sass
-
# Sass cache stores are in charge of storing cached information,
-
# especially parse trees for Sass documents.
-
#
-
# User-created importers must inherit from {CacheStores::Base}.
-
1
module CacheStores
-
end
-
end
-
-
1
require 'sass/cache_stores/base'
-
1
require 'sass/cache_stores/filesystem'
-
1
require 'sass/cache_stores/memory'
-
1
require 'sass/cache_stores/chain'
-
1
module Sass
-
1
module CacheStores
-
# An abstract base class for backends for the Sass cache.
-
# Any key-value store can act as such a backend;
-
# it just needs to implement the
-
# \{#_store} and \{#_retrieve} methods.
-
#
-
# To use a cache store with Sass,
-
# use the {file:SASS_REFERENCE.md#cache_store-option `:cache_store` option}.
-
#
-
# @abstract
-
1
class Base
-
# Store cached contents for later retrieval
-
# Must be implemented by all CacheStore subclasses
-
#
-
# Note: cache contents contain binary data.
-
#
-
# @param key [String] The key to store the contents under
-
# @param version [String] The current sass version.
-
# Cached contents must not be retrieved across different versions of sass.
-
# @param sha [String] The sha of the sass source.
-
# Cached contents must not be retrieved if the sha has changed.
-
# @param contents [String] The contents to store.
-
1
def _store(key, version, sha, contents)
-
raise "#{self.class} must implement #_store."
-
end
-
-
# Retrieved cached contents.
-
# Must be implemented by all subclasses.
-
#
-
# Note: if the key exists but the sha or version have changed,
-
# then the key may be deleted by the cache store, if it wants to do so.
-
#
-
# @param key [String] The key to retrieve
-
# @param version [String] The current sass version.
-
# Cached contents must not be retrieved across different versions of sass.
-
# @param sha [String] The sha of the sass source.
-
# Cached contents must not be retrieved if the sha has changed.
-
# @return [String] The contents that were previously stored.
-
# @return [NilClass] when the cache key is not found or the version or sha have changed.
-
1
def _retrieve(key, version, sha)
-
raise "#{self.class} must implement #_retrieve."
-
end
-
-
# Store a {Sass::Tree::RootNode}.
-
#
-
# @param key [String] The key to store it under.
-
# @param sha [String] The checksum for the contents that are being stored.
-
# @param obj [Object] The object to cache.
-
1
def store(key, sha, root)
-
_store(key, Sass::VERSION, sha, Marshal.dump(root))
-
rescue TypeError, LoadError => e
-
Sass::Util.sass_warn "Warning. Error encountered while saving cache #{path_to(key)}: #{e}"
-
end
-
-
# Retrieve a {Sass::Tree::RootNode}.
-
#
-
# @param key [String] The key the root element was stored under.
-
# @param sha [String] The checksum of the root element's content.
-
# @return [Object] The cached object.
-
1
def retrieve(key, sha)
-
contents = _retrieve(key, Sass::VERSION, sha)
-
Marshal.load(contents) if contents
-
rescue EOFError, TypeError, ArgumentError, LoadError => e
-
Sass::Util.sass_warn "Warning. Error encountered while reading cache #{path_to(key)}: #{e}"
-
end
-
-
# Return the key for the sass file.
-
#
-
# The `(sass_dirname, sass_basename)` pair
-
# should uniquely identify the Sass document,
-
# but otherwise there are no restrictions on their content.
-
#
-
# @param sass_dirname [String]
-
# The fully-expanded location of the Sass file.
-
# This corresponds to the directory name on a filesystem.
-
# @param sass_basename [String] The name of the Sass file that is being referenced.
-
# This corresponds to the basename on a filesystem.
-
1
def key(sass_dirname, sass_basename)
-
dir = Digest::SHA1.hexdigest(sass_dirname)
-
filename = "#{sass_basename}c"
-
"#{dir}/#{filename}"
-
end
-
end
-
end
-
end
-
1
module Sass
-
1
module CacheStores
-
# A meta-cache that chains multiple caches together.
-
# Specifically:
-
#
-
# * All `#store`s are passed to all caches.
-
# * `#retrieve`s are passed to each cache until one has a hit.
-
# * When one cache has a hit, the value is `#store`d in all earlier caches.
-
1
class Chain < Base
-
# Create a new cache chaining the given caches.
-
#
-
# @param caches [Array<Sass::CacheStores::Base>] The caches to chain.
-
1
def initialize(*caches)
-
@caches = caches
-
end
-
-
# @see Base#store
-
1
def store(key, sha, obj)
-
@caches.each {|c| c.store(key, sha, obj)}
-
end
-
-
# @see Base#retrieve
-
1
def retrieve(key, sha)
-
@caches.each_with_index do |c, i|
-
next unless obj = c.retrieve(key, sha)
-
@caches[0...i].each {|c| c.store(key, sha, obj)}
-
return obj
-
end
-
nil
-
end
-
end
-
end
-
end
-
1
require 'fileutils'
-
-
1
module Sass
-
1
module CacheStores
-
# A backend for the Sass cache using the filesystem.
-
1
class Filesystem < Base
-
# The directory where the cached files will be stored.
-
#
-
# @return [String]
-
1
attr_accessor :cache_location
-
-
# @param cache_location [String] see \{#cache\_location}
-
1
def initialize(cache_location)
-
@cache_location = cache_location
-
end
-
-
# @see Base#\_retrieve
-
1
def _retrieve(key, version, sha)
-
return unless File.readable?(path_to(key))
-
contents = nil
-
File.open(path_to(key), "rb") do |f|
-
if f.readline("\n").strip == version && f.readline("\n").strip == sha
-
return f.read
-
end
-
end
-
File.unlink path_to(key)
-
nil
-
rescue EOFError, TypeError, ArgumentError => e
-
Sass::Util.sass_warn "Warning. Error encountered while reading cache #{path_to(key)}: #{e}"
-
end
-
-
# @see Base#\_store
-
1
def _store(key, version, sha, contents)
-
# return unless File.writable?(File.dirname(@cache_location))
-
# return if File.exists?(@cache_location) && !File.writable?(@cache_location)
-
compiled_filename = path_to(key)
-
# return if File.exists?(File.dirname(compiled_filename)) && !File.writable?(File.dirname(compiled_filename))
-
# return if File.exists?(compiled_filename) && !File.writable?(compiled_filename)
-
FileUtils.mkdir_p(File.dirname(compiled_filename))
-
File.open(compiled_filename, "wb") do |f|
-
f.puts(version)
-
f.puts(sha)
-
f.write(contents)
-
end
-
rescue Errno::EACCES
-
#pass
-
end
-
-
1
private
-
-
# Returns the path to a file for the given key.
-
#
-
# @param key [String]
-
# @return [String] The path to the cache file.
-
1
def path_to(key)
-
key = key.gsub(/[<>:\\|?*%]/) {|c| "%%%03d" % Sass::Util.ord(c)}
-
File.join(cache_location, key)
-
end
-
end
-
end
-
end
-
1
module Sass
-
1
module CacheStores
-
# A backend for the Sass cache using in-process memory.
-
1
class Memory < Base
-
# Since the {Memory} store is stored in the Sass tree's options hash,
-
# when the options get serialized as part of serializing the tree,
-
# you get crazy exponential growth in the size of the cached objects
-
# unless you don't dump the cache.
-
#
-
# @private
-
1
def _dump(depth)
-
""
-
end
-
-
# If we deserialize this class, just make a new empty one.
-
#
-
# @private
-
1
def self._load(repr)
-
Memory.new
-
end
-
-
# Create a new, empty cache store.
-
1
def initialize
-
@contents = {}
-
end
-
-
# @see Base#retrieve
-
1
def retrieve(key, sha)
-
if @contents.has_key?(key)
-
return unless @contents[key][:sha] == sha
-
obj = @contents[key][:obj]
-
obj.respond_to?(:deep_copy) ? obj.deep_copy : obj.dup
-
end
-
end
-
-
# @see Base#store
-
1
def store(key, sha, obj)
-
@contents[key] = {:sha => sha, :obj => obj}
-
end
-
-
# Destructively clear the cache.
-
1
def reset!
-
@contents = {}
-
end
-
end
-
end
-
end
-
1
require 'set'
-
1
require 'digest/sha1'
-
1
require 'sass/cache_stores'
-
1
require 'sass/tree/node'
-
1
require 'sass/tree/root_node'
-
1
require 'sass/tree/rule_node'
-
1
require 'sass/tree/comment_node'
-
1
require 'sass/tree/prop_node'
-
1
require 'sass/tree/directive_node'
-
1
require 'sass/tree/media_node'
-
1
require 'sass/tree/variable_node'
-
1
require 'sass/tree/mixin_def_node'
-
1
require 'sass/tree/mixin_node'
-
1
require 'sass/tree/function_node'
-
1
require 'sass/tree/return_node'
-
1
require 'sass/tree/extend_node'
-
1
require 'sass/tree/if_node'
-
1
require 'sass/tree/while_node'
-
1
require 'sass/tree/for_node'
-
1
require 'sass/tree/each_node'
-
1
require 'sass/tree/debug_node'
-
1
require 'sass/tree/warn_node'
-
1
require 'sass/tree/import_node'
-
1
require 'sass/tree/charset_node'
-
1
require 'sass/tree/visitors/base'
-
1
require 'sass/tree/visitors/perform'
-
1
require 'sass/tree/visitors/cssize'
-
1
require 'sass/tree/visitors/convert'
-
1
require 'sass/tree/visitors/to_css'
-
1
require 'sass/tree/visitors/deep_copy'
-
1
require 'sass/tree/visitors/set_options'
-
1
require 'sass/tree/visitors/check_nesting'
-
1
require 'sass/selector'
-
1
require 'sass/environment'
-
1
require 'sass/script'
-
1
require 'sass/scss'
-
1
require 'sass/error'
-
1
require 'sass/importers'
-
1
require 'sass/shared'
-
-
1
module Sass
-
-
# A Sass mixin or function.
-
#
-
# `name`: `String`
-
# : The name of the mixin/function.
-
#
-
# `args`: `Array<(String, Script::Node)>`
-
# : The arguments for the mixin/function.
-
# Each element is a tuple containing the name of the argument
-
# and the parse tree for the default value of the argument.
-
#
-
# `environment`: {Sass::Environment}
-
# : The environment in which the mixin/function was defined.
-
# This is captured so that the mixin/function can have access
-
# to local variables defined in its scope.
-
#
-
# `tree`: `Array<Tree::Node>`
-
# : The parse tree for the mixin/function.
-
1
Callable = Struct.new(:name, :args, :environment, :tree)
-
-
# This class handles the parsing and compilation of the Sass template.
-
# Example usage:
-
#
-
# template = File.load('stylesheets/sassy.sass')
-
# sass_engine = Sass::Engine.new(template)
-
# output = sass_engine.render
-
# puts output
-
1
class Engine
-
1
include Sass::Util
-
-
# A line of Sass code.
-
#
-
# `text`: `String`
-
# : The text in the line, without any whitespace at the beginning or end.
-
#
-
# `tabs`: `Fixnum`
-
# : The level of indentation of the line.
-
#
-
# `index`: `Fixnum`
-
# : The line number in the original document.
-
#
-
# `offset`: `Fixnum`
-
# : The number of bytes in on the line that the text begins.
-
# This ends up being the number of bytes of leading whitespace.
-
#
-
# `filename`: `String`
-
# : The name of the file in which this line appeared.
-
#
-
# `children`: `Array<Line>`
-
# : The lines nested below this one.
-
#
-
# `comment_tab_str`: `String?`
-
# : The prefix indentation for this comment, if it is a comment.
-
1
class Line < Struct.new(:text, :tabs, :index, :offset, :filename, :children, :comment_tab_str)
-
1
def comment?
-
text[0] == COMMENT_CHAR && (text[1] == SASS_COMMENT_CHAR || text[1] == CSS_COMMENT_CHAR)
-
end
-
end
-
-
# The character that begins a CSS property.
-
1
PROPERTY_CHAR = ?:
-
-
# The character that designates the beginning of a comment,
-
# either Sass or CSS.
-
1
COMMENT_CHAR = ?/
-
-
# The character that follows the general COMMENT_CHAR and designates a Sass comment,
-
# which is not output as a CSS comment.
-
1
SASS_COMMENT_CHAR = ?/
-
-
# The character that indicates that a comment allows interpolation
-
# and should be preserved even in `:compressed` mode.
-
1
SASS_LOUD_COMMENT_CHAR = ?!
-
-
# The character that follows the general COMMENT_CHAR and designates a CSS comment,
-
# which is embedded in the CSS document.
-
1
CSS_COMMENT_CHAR = ?*
-
-
# The character used to denote a compiler directive.
-
1
DIRECTIVE_CHAR = ?@
-
-
# Designates a non-parsed rule.
-
1
ESCAPE_CHAR = ?\\
-
-
# Designates block as mixin definition rather than CSS rules to output
-
1
MIXIN_DEFINITION_CHAR = ?=
-
-
# Includes named mixin declared using MIXIN_DEFINITION_CHAR
-
1
MIXIN_INCLUDE_CHAR = ?+
-
-
# The regex that matches and extracts data from
-
# properties of the form `:name prop`.
-
1
PROPERTY_OLD = /^:([^\s=:"]+)\s*(?:\s+|$)(.*)/
-
-
# The default options for Sass::Engine.
-
# @api public
-
1
DEFAULT_OPTIONS = {
-
:style => :nested,
-
:load_paths => ['.'],
-
:cache => true,
-
:cache_location => './.sass-cache',
-
:syntax => :sass,
-
:filesystem_importer => Sass::Importers::Filesystem
-
}.freeze
-
-
# Converts a Sass options hash into a standard form, filling in
-
# default values and resolving aliases.
-
#
-
# @param options [{Symbol => Object}] The options hash;
-
# see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
-
# @return [{Symbol => Object}] The normalized options hash.
-
# @private
-
1
def self.normalize_options(options)
-
options = DEFAULT_OPTIONS.merge(options.reject {|k, v| v.nil?})
-
-
# If the `:filename` option is passed in without an importer,
-
# assume it's using the default filesystem importer.
-
options[:importer] ||= options[:filesystem_importer].new(".") if options[:filename]
-
-
# Tracks the original filename of the top-level Sass file
-
options[:original_filename] ||= options[:filename]
-
-
options[:cache_store] ||= Sass::CacheStores::Chain.new(
-
Sass::CacheStores::Memory.new, Sass::CacheStores::Filesystem.new(options[:cache_location]))
-
# Support both, because the docs said one and the other actually worked
-
# for quite a long time.
-
options[:line_comments] ||= options[:line_numbers]
-
-
options[:load_paths] = options[:load_paths].map do |p|
-
next p unless p.is_a?(String) || (defined?(Pathname) && p.is_a?(Pathname))
-
options[:filesystem_importer].new(p.to_s)
-
end
-
-
# Backwards compatibility
-
options[:property_syntax] ||= options[:attribute_syntax]
-
case options[:property_syntax]
-
when :alternate; options[:property_syntax] = :new
-
when :normal; options[:property_syntax] = :old
-
end
-
-
options
-
end
-
-
# Returns the {Sass::Engine} for the given file.
-
# This is preferable to Sass::Engine.new when reading from a file
-
# because it properly sets up the Engine's metadata,
-
# enables parse-tree caching,
-
# and infers the syntax from the filename.
-
#
-
# @param filename [String] The path to the Sass or SCSS file
-
# @param options [{Symbol => Object}] The options hash;
-
# See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
-
# @return [Sass::Engine] The Engine for the given Sass or SCSS file.
-
# @raise [Sass::SyntaxError] if there's an error in the document.
-
1
def self.for_file(filename, options)
-
had_syntax = options[:syntax]
-
-
if had_syntax
-
# Use what was explicitly specificed
-
elsif filename =~ /\.scss$/
-
options.merge!(:syntax => :scss)
-
elsif filename =~ /\.sass$/
-
options.merge!(:syntax => :sass)
-
end
-
-
Sass::Engine.new(File.read(filename), options.merge(:filename => filename))
-
end
-
-
# The options for the Sass engine.
-
# See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
-
#
-
# @return [{Symbol => Object}]
-
1
attr_reader :options
-
-
# Creates a new Engine. Note that Engine should only be used directly
-
# when compiling in-memory Sass code.
-
# If you're compiling a single Sass file from the filesystem,
-
# use \{Sass::Engine.for\_file}.
-
# If you're compiling multiple files from the filesystem,
-
# use {Sass::Plugin}.
-
#
-
# @param template [String] The Sass template.
-
# This template can be encoded using any encoding
-
# that can be converted to Unicode.
-
# If the template contains an `@charset` declaration,
-
# that overrides the Ruby encoding
-
# (see {file:SASS_REFERENCE.md#encodings the encoding documentation})
-
# @param options [{Symbol => Object}] An options hash.
-
# See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
-
# @see {Sass::Engine.for_file}
-
# @see {Sass::Plugin}
-
1
def initialize(template, options={})
-
@options = self.class.normalize_options(options)
-
@template = template
-
end
-
-
# Render the template to CSS.
-
#
-
# @return [String] The CSS
-
# @raise [Sass::SyntaxError] if there's an error in the document
-
# @raise [Encoding::UndefinedConversionError] if the source encoding
-
# cannot be converted to UTF-8
-
# @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
-
1
def render
-
return _render unless @options[:quiet]
-
Sass::Util.silence_sass_warnings {_render}
-
end
-
1
alias_method :to_css, :render
-
-
# Parses the document into its parse tree. Memoized.
-
#
-
# @return [Sass::Tree::Node] The root of the parse tree.
-
# @raise [Sass::SyntaxError] if there's an error in the document
-
1
def to_tree
-
@tree ||= @options[:quiet] ?
-
Sass::Util.silence_sass_warnings {_to_tree} :
-
_to_tree
-
end
-
-
# Returns the original encoding of the document,
-
# or `nil` under Ruby 1.8.
-
#
-
# @return [Encoding, nil]
-
# @raise [Encoding::UndefinedConversionError] if the source encoding
-
# cannot be converted to UTF-8
-
# @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
-
1
def source_encoding
-
check_encoding!
-
@original_encoding
-
end
-
-
# Gets a set of all the documents
-
# that are (transitive) dependencies of this document,
-
# not including the document itself.
-
#
-
# @return [[Sass::Engine]] The dependency documents.
-
1
def dependencies
-
_dependencies(Set.new, engines = Set.new)
-
engines - [self]
-
end
-
-
# Helper for \{#dependencies}.
-
#
-
# @private
-
1
def _dependencies(seen, engines)
-
return if seen.include?(key = [@options[:filename], @options[:importer]])
-
seen << key
-
engines << self
-
to_tree.grep(Tree::ImportNode) do |n|
-
next if n.css_import?
-
n.imported_file._dependencies(seen, engines)
-
end
-
end
-
-
1
private
-
-
1
def _render
-
rendered = _to_tree.render
-
return rendered if ruby1_8?
-
begin
-
# Try to convert the result to the original encoding,
-
# but if that doesn't work fall back on UTF-8
-
rendered = rendered.encode(source_encoding)
-
rescue EncodingError
-
end
-
rendered.gsub(Regexp.new('\A@charset "(.*?)"'.encode(source_encoding)),
-
"@charset \"#{source_encoding.name}\"".encode(source_encoding))
-
end
-
-
1
def _to_tree
-
if (@options[:cache] || @options[:read_cache]) &&
-
@options[:filename] && @options[:importer]
-
key = sassc_key
-
sha = Digest::SHA1.hexdigest(@template)
-
-
if root = @options[:cache_store].retrieve(key, sha)
-
root.options = @options
-
return root
-
end
-
end
-
-
check_encoding!
-
-
if @options[:syntax] == :scss
-
root = Sass::SCSS::Parser.new(@template, @options[:filename]).parse
-
else
-
root = Tree::RootNode.new(@template)
-
append_children(root, tree(tabulate(@template)).first, true)
-
end
-
-
root.options = @options
-
if @options[:cache] && key && sha
-
begin
-
old_options = root.options
-
root.options = {}
-
@options[:cache_store].store(key, sha, root)
-
ensure
-
root.options = old_options
-
end
-
end
-
root
-
rescue SyntaxError => e
-
e.modify_backtrace(:filename => @options[:filename], :line => @line)
-
e.sass_template = @template
-
raise e
-
end
-
-
1
def sassc_key
-
@options[:cache_store].key(*@options[:importer].key(@options[:filename], @options))
-
end
-
-
1
def check_encoding!
-
return if @checked_encoding
-
@checked_encoding = true
-
@template, @original_encoding = check_sass_encoding(@template) do |msg, line|
-
raise Sass::SyntaxError.new(msg, :line => line)
-
end
-
end
-
-
1
def tabulate(string)
-
tab_str = nil
-
comment_tab_str = nil
-
first = true
-
lines = []
-
string.gsub(/\r|\n|\r\n|\r\n/, "\n").scan(/^[^\n]*?$/).each_with_index do |line, index|
-
index += (@options[:line] || 1)
-
if line.strip.empty?
-
lines.last.text << "\n" if lines.last && lines.last.comment?
-
next
-
end
-
-
line_tab_str = line[/^\s*/]
-
unless line_tab_str.empty?
-
if tab_str.nil?
-
comment_tab_str ||= line_tab_str
-
next if try_comment(line, lines.last, "", comment_tab_str, index)
-
comment_tab_str = nil
-
end
-
-
tab_str ||= line_tab_str
-
-
raise SyntaxError.new("Indenting at the beginning of the document is illegal.",
-
:line => index) if first
-
-
raise SyntaxError.new("Indentation can't use both tabs and spaces.",
-
:line => index) if tab_str.include?(?\s) && tab_str.include?(?\t)
-
end
-
first &&= !tab_str.nil?
-
if tab_str.nil?
-
lines << Line.new(line.strip, 0, index, 0, @options[:filename], [])
-
next
-
end
-
-
comment_tab_str ||= line_tab_str
-
if try_comment(line, lines.last, tab_str * lines.last.tabs, comment_tab_str, index)
-
next
-
else
-
comment_tab_str = nil
-
end
-
-
line_tabs = line_tab_str.scan(tab_str).size
-
if tab_str * line_tabs != line_tab_str
-
message = <<END.strip.gsub("\n", ' ')
-
Inconsistent indentation: #{Sass::Shared.human_indentation line_tab_str, true} used for indentation,
-
but the rest of the document was indented using #{Sass::Shared.human_indentation tab_str}.
-
END
-
raise SyntaxError.new(message, :line => index)
-
end
-
-
lines << Line.new(line.strip, line_tabs, index, tab_str.size, @options[:filename], [])
-
end
-
lines
-
end
-
-
1
def try_comment(line, last, tab_str, comment_tab_str, index)
-
return unless last && last.comment?
-
# Nested comment stuff must be at least one whitespace char deeper
-
# than the normal indentation
-
return unless line =~ /^#{tab_str}\s/
-
unless line =~ /^(?:#{comment_tab_str})(.*)$/
-
raise SyntaxError.new(<<MSG.strip.gsub("\n", " "), :line => index)
-
Inconsistent indentation:
-
previous line was indented by #{Sass::Shared.human_indentation comment_tab_str},
-
but this line was indented by #{Sass::Shared.human_indentation line[/^\s*/]}.
-
MSG
-
end
-
-
last.comment_tab_str ||= comment_tab_str
-
last.text << "\n" << line
-
true
-
end
-
-
1
def tree(arr, i = 0)
-
return [], i if arr[i].nil?
-
-
base = arr[i].tabs
-
nodes = []
-
while (line = arr[i]) && line.tabs >= base
-
if line.tabs > base
-
raise SyntaxError.new("The line was indented #{line.tabs - base} levels deeper than the previous line.",
-
:line => line.index) if line.tabs > base + 1
-
-
nodes.last.children, i = tree(arr, i)
-
else
-
nodes << line
-
i += 1
-
end
-
end
-
return nodes, i
-
end
-
-
1
def build_tree(parent, line, root = false)
-
@line = line.index
-
node_or_nodes = parse_line(parent, line, root)
-
-
Array(node_or_nodes).each do |node|
-
# Node is a symbol if it's non-outputting, like a variable assignment
-
next unless node.is_a? Tree::Node
-
-
node.line = line.index
-
node.filename = line.filename
-
-
append_children(node, line.children, false)
-
end
-
-
node_or_nodes
-
end
-
-
1
def append_children(parent, children, root)
-
continued_rule = nil
-
continued_comment = nil
-
children.each do |line|
-
child = build_tree(parent, line, root)
-
-
if child.is_a?(Tree::RuleNode)
-
if child.continued? && child.children.empty?
-
if continued_rule
-
continued_rule.add_rules child
-
else
-
continued_rule = child
-
end
-
next
-
elsif continued_rule
-
continued_rule.add_rules child
-
continued_rule.children = child.children
-
continued_rule, child = nil, continued_rule
-
end
-
elsif continued_rule
-
continued_rule = nil
-
end
-
-
if child.is_a?(Tree::CommentNode) && child.silent
-
if continued_comment &&
-
child.line == continued_comment.line +
-
continued_comment.lines + 1
-
continued_comment.value += ["\n"] + child.value
-
next
-
end
-
-
continued_comment = child
-
end
-
-
check_for_no_children(child)
-
validate_and_append_child(parent, child, line, root)
-
end
-
-
parent
-
end
-
-
1
def validate_and_append_child(parent, child, line, root)
-
case child
-
when Array
-
child.each {|c| validate_and_append_child(parent, c, line, root)}
-
when Tree::Node
-
parent << child
-
end
-
end
-
-
1
def check_for_no_children(node)
-
return unless node.is_a?(Tree::RuleNode) && node.children.empty?
-
Sass::Util.sass_warn(<<WARNING.strip)
-
WARNING on line #{node.line}#{" of #{node.filename}" if node.filename}:
-
This selector doesn't have any properties and will not be rendered.
-
WARNING
-
end
-
-
1
def parse_line(parent, line, root)
-
case line.text[0]
-
when PROPERTY_CHAR
-
if line.text[1] == PROPERTY_CHAR ||
-
(@options[:property_syntax] == :new &&
-
line.text =~ PROPERTY_OLD && $2.empty?)
-
# Support CSS3-style pseudo-elements,
-
# which begin with ::,
-
# as well as pseudo-classes
-
# if we're using the new property syntax
-
Tree::RuleNode.new(parse_interp(line.text))
-
else
-
name, value = line.text.scan(PROPERTY_OLD)[0]
-
raise SyntaxError.new("Invalid property: \"#{line.text}\".",
-
:line => @line) if name.nil? || value.nil?
-
parse_property(name, parse_interp(name), value, :old, line)
-
end
-
when ?$
-
parse_variable(line)
-
when COMMENT_CHAR
-
parse_comment(line)
-
when DIRECTIVE_CHAR
-
parse_directive(parent, line, root)
-
when ESCAPE_CHAR
-
Tree::RuleNode.new(parse_interp(line.text[1..-1]))
-
when MIXIN_DEFINITION_CHAR
-
parse_mixin_definition(line)
-
when MIXIN_INCLUDE_CHAR
-
if line.text[1].nil? || line.text[1] == ?\s
-
Tree::RuleNode.new(parse_interp(line.text))
-
else
-
parse_mixin_include(line, root)
-
end
-
else
-
parse_property_or_rule(line)
-
end
-
end
-
-
1
def parse_property_or_rule(line)
-
scanner = Sass::Util::MultibyteStringScanner.new(line.text)
-
hack_char = scanner.scan(/[:\*\.]|\#(?!\{)/)
-
parser = Sass::SCSS::SassParser.new(scanner, @options[:filename], @line)
-
-
unless res = parser.parse_interp_ident
-
return Tree::RuleNode.new(parse_interp(line.text))
-
end
-
res.unshift(hack_char) if hack_char
-
if comment = scanner.scan(Sass::SCSS::RX::COMMENT)
-
res << comment
-
end
-
-
name = line.text[0...scanner.pos]
-
if scanner.scan(/\s*:(?:\s|$)/)
-
parse_property(name, res, scanner.rest, :new, line)
-
else
-
res.pop if comment
-
Tree::RuleNode.new(res + parse_interp(scanner.rest))
-
end
-
end
-
-
1
def parse_property(name, parsed_name, value, prop, line)
-
if value.strip.empty?
-
expr = Sass::Script::String.new("")
-
else
-
expr = parse_script(value, :offset => line.offset + line.text.index(value))
-
end
-
Tree::PropNode.new(parse_interp(name), expr, prop)
-
end
-
-
1
def parse_variable(line)
-
name, value, default = line.text.scan(Script::MATCH)[0]
-
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath variable declarations.",
-
:line => @line + 1) unless line.children.empty?
-
raise SyntaxError.new("Invalid variable: \"#{line.text}\".",
-
:line => @line) unless name && value
-
-
expr = parse_script(value, :offset => line.offset + line.text.index(value))
-
-
Tree::VariableNode.new(name, expr, default)
-
end
-
-
1
def parse_comment(line)
-
if line.text[1] == CSS_COMMENT_CHAR || line.text[1] == SASS_COMMENT_CHAR
-
silent = line.text[1] == SASS_COMMENT_CHAR
-
if loud = line.text[2] == SASS_LOUD_COMMENT_CHAR
-
value = self.class.parse_interp(line.text, line.index, line.offset, :filename => @filename)
-
value[0].slice!(2) # get rid of the "!"
-
else
-
value = [line.text]
-
end
-
value = with_extracted_values(value) do |str|
-
str = str.gsub(/^#{line.comment_tab_str}/m, '')[2..-1] # get rid of // or /*
-
format_comment_text(str, silent)
-
end
-
Tree::CommentNode.new(value, silent, loud)
-
else
-
Tree::RuleNode.new(parse_interp(line.text))
-
end
-
end
-
-
1
def parse_directive(parent, line, root)
-
directive, whitespace, value = line.text[1..-1].split(/(\s+)/, 2)
-
offset = directive.size + whitespace.size + 1 if whitespace
-
-
# If value begins with url( or ",
-
# it's a CSS @import rule and we don't want to touch it.
-
if directive == "import"
-
parse_import(line, value)
-
elsif directive == "mixin"
-
parse_mixin_definition(line)
-
elsif directive == "include"
-
parse_mixin_include(line, root)
-
elsif directive == "function"
-
parse_function(line, root)
-
elsif directive == "for"
-
parse_for(line, root, value)
-
elsif directive == "each"
-
parse_each(line, root, value)
-
elsif directive == "else"
-
parse_else(parent, line, value)
-
elsif directive == "while"
-
raise SyntaxError.new("Invalid while directive '@while': expected expression.") unless value
-
Tree::WhileNode.new(parse_script(value, :offset => offset))
-
elsif directive == "if"
-
raise SyntaxError.new("Invalid if directive '@if': expected expression.") unless value
-
Tree::IfNode.new(parse_script(value, :offset => offset))
-
elsif directive == "debug"
-
raise SyntaxError.new("Invalid debug directive '@debug': expected expression.") unless value
-
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath debug directives.",
-
:line => @line + 1) unless line.children.empty?
-
offset = line.offset + line.text.index(value).to_i
-
Tree::DebugNode.new(parse_script(value, :offset => offset))
-
elsif directive == "extend"
-
raise SyntaxError.new("Invalid extend directive '@extend': expected expression.") unless value
-
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath extend directives.",
-
:line => @line + 1) unless line.children.empty?
-
offset = line.offset + line.text.index(value).to_i
-
Tree::ExtendNode.new(parse_interp(value, offset))
-
elsif directive == "warn"
-
raise SyntaxError.new("Invalid warn directive '@warn': expected expression.") unless value
-
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath warn directives.",
-
:line => @line + 1) unless line.children.empty?
-
offset = line.offset + line.text.index(value).to_i
-
Tree::WarnNode.new(parse_script(value, :offset => offset))
-
elsif directive == "return"
-
raise SyntaxError.new("Invalid @return: expected expression.") unless value
-
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath return directives.",
-
:line => @line + 1) unless line.children.empty?
-
offset = line.offset + line.text.index(value).to_i
-
Tree::ReturnNode.new(parse_script(value, :offset => offset))
-
elsif directive == "charset"
-
name = value && value[/\A(["'])(.*)\1\Z/, 2] #"
-
raise SyntaxError.new("Invalid charset directive '@charset': expected string.") unless name
-
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath charset directives.",
-
:line => @line + 1) unless line.children.empty?
-
Tree::CharsetNode.new(name)
-
elsif directive == "media"
-
Tree::MediaNode.new(value.split(',').map {|s| s.strip})
-
else
-
Tree::DirectiveNode.new(line.text)
-
end
-
end
-
-
1
def parse_for(line, root, text)
-
var, from_expr, to_name, to_expr = text.scan(/^([^\s]+)\s+from\s+(.+)\s+(to|through)\s+(.+)$/).first
-
-
if var.nil? # scan failed, try to figure out why for error message
-
if text !~ /^[^\s]+/
-
expected = "variable name"
-
elsif text !~ /^[^\s]+\s+from\s+.+/
-
expected = "'from <expr>'"
-
else
-
expected = "'to <expr>' or 'through <expr>'"
-
end
-
raise SyntaxError.new("Invalid for directive '@for #{text}': expected #{expected}.")
-
end
-
raise SyntaxError.new("Invalid variable \"#{var}\".") unless var =~ Script::VALIDATE
-
-
var = var[1..-1]
-
parsed_from = parse_script(from_expr, :offset => line.offset + line.text.index(from_expr))
-
parsed_to = parse_script(to_expr, :offset => line.offset + line.text.index(to_expr))
-
Tree::ForNode.new(var, parsed_from, parsed_to, to_name == 'to')
-
end
-
-
1
def parse_each(line, root, text)
-
var, list_expr = text.scan(/^([^\s]+)\s+in\s+(.+)$/).first
-
-
if var.nil? # scan failed, try to figure out why for error message
-
if text !~ /^[^\s]+/
-
expected = "variable name"
-
elsif text !~ /^[^\s]+\s+from\s+.+/
-
expected = "'in <expr>'"
-
end
-
raise SyntaxError.new("Invalid for directive '@each #{text}': expected #{expected}.")
-
end
-
raise SyntaxError.new("Invalid variable \"#{var}\".") unless var =~ Script::VALIDATE
-
-
var = var[1..-1]
-
parsed_list = parse_script(list_expr, :offset => line.offset + line.text.index(list_expr))
-
Tree::EachNode.new(var, parsed_list)
-
end
-
-
1
def parse_else(parent, line, text)
-
previous = parent.children.last
-
raise SyntaxError.new("@else must come after @if.") unless previous.is_a?(Tree::IfNode)
-
-
if text
-
if text !~ /^if\s+(.+)/
-
raise SyntaxError.new("Invalid else directive '@else #{text}': expected 'if <expr>'.")
-
end
-
expr = parse_script($1, :offset => line.offset + line.text.index($1))
-
end
-
-
node = Tree::IfNode.new(expr)
-
append_children(node, line.children, false)
-
previous.add_else node
-
nil
-
end
-
-
1
def parse_import(line, value)
-
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath import directives.",
-
:line => @line + 1) unless line.children.empty?
-
-
scanner = Sass::Util::MultibyteStringScanner.new(value)
-
values = []
-
-
loop do
-
unless node = parse_import_arg(scanner)
-
raise SyntaxError.new("Invalid @import: expected file to import, was #{scanner.rest.inspect}",
-
:line => @line)
-
end
-
values << node
-
break unless scanner.scan(/,\s*/)
-
end
-
-
if scanner.scan(/;/)
-
raise SyntaxError.new("Invalid @import: expected end of line, was \";\".",
-
:line => @line)
-
end
-
-
return values
-
end
-
-
1
def parse_import_arg(scanner)
-
return if scanner.eos?
-
unless (str = scanner.scan(Sass::SCSS::RX::STRING)) ||
-
(uri = scanner.scan(Sass::SCSS::RX::URI))
-
return Tree::ImportNode.new(scanner.scan(/[^,;]+/))
-
end
-
-
val = scanner[1] || scanner[2]
-
scanner.scan(/\s*/)
-
if media = scanner.scan(/[a-zA-Z].*/)
-
Tree::DirectiveNode.new("@import #{str || uri} #{media}")
-
elsif !scanner.match?(/[,;]|$/)
-
raise SyntaxError.new("Invalid @import: \"#{str || uri} #{scanner.rest}\"")
-
elsif uri
-
Tree::DirectiveNode.new("@import #{uri}")
-
elsif val =~ /^http:\/\//
-
Tree::DirectiveNode.new("@import #{str}")
-
else
-
Tree::ImportNode.new(val)
-
end
-
end
-
-
1
MIXIN_DEF_RE = /^(?:=|@mixin)\s*(#{Sass::SCSS::RX::IDENT})(.*)$/
-
1
def parse_mixin_definition(line)
-
name, arg_string = line.text.scan(MIXIN_DEF_RE).first
-
raise SyntaxError.new("Invalid mixin \"#{line.text[1..-1]}\".") if name.nil?
-
-
offset = line.offset + line.text.size - arg_string.size
-
args = Script::Parser.new(arg_string.strip, @line, offset, @options).
-
parse_mixin_definition_arglist
-
Tree::MixinDefNode.new(name, args)
-
end
-
-
1
MIXIN_INCLUDE_RE = /^(?:\+|@include)\s*(#{Sass::SCSS::RX::IDENT})(.*)$/
-
1
def parse_mixin_include(line, root)
-
name, arg_string = line.text.scan(MIXIN_INCLUDE_RE).first
-
raise SyntaxError.new("Invalid mixin include \"#{line.text}\".") if name.nil?
-
-
offset = line.offset + line.text.size - arg_string.size
-
args, keywords = Script::Parser.new(arg_string.strip, @line, offset, @options).
-
parse_mixin_include_arglist
-
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath mixin directives.",
-
:line => @line + 1) unless line.children.empty?
-
Tree::MixinNode.new(name, args, keywords)
-
end
-
-
1
FUNCTION_RE = /^@function\s*(#{Sass::SCSS::RX::IDENT})(.*)$/
-
1
def parse_function(line, root)
-
name, arg_string = line.text.scan(FUNCTION_RE).first
-
raise SyntaxError.new("Invalid function definition \"#{line.text}\".") if name.nil?
-
-
offset = line.offset + line.text.size - arg_string.size
-
args = Script::Parser.new(arg_string.strip, @line, offset, @options).
-
parse_function_definition_arglist
-
Tree::FunctionNode.new(name, args)
-
end
-
-
1
def parse_script(script, options = {})
-
line = options[:line] || @line
-
offset = options[:offset] || 0
-
Script.parse(script, line, offset, @options)
-
end
-
-
1
def format_comment_text(text, silent)
-
content = text.split("\n")
-
-
if content.first && content.first.strip.empty?
-
removed_first = true
-
content.shift
-
end
-
-
return silent ? "//" : "/* */" if content.empty?
-
content.last.gsub!(%r{ ?\*/ *$}, '')
-
content.map! {|l| l.gsub!(/^\*( ?)/, '\1') || (l.empty? ? "" : " ") + l}
-
content.first.gsub!(/^ /, '') unless removed_first
-
if silent
-
"//" + content.join("\n//")
-
else
-
# The #gsub fixes the case of a trailing */
-
"/*" + content.join("\n *").gsub(/ \*\Z/, '') + " */"
-
end
-
end
-
-
1
def parse_interp(text, offset = 0)
-
self.class.parse_interp(text, @line, offset, :filename => @filename)
-
end
-
-
# It's important that this have strings (at least)
-
# at the beginning, the end, and between each Script::Node.
-
#
-
# @private
-
1
def self.parse_interp(text, line, offset, options)
-
res = []
-
rest = Sass::Shared.handle_interpolation text do |scan|
-
escapes = scan[2].size
-
res << scan.matched[0...-2 - escapes]
-
if escapes % 2 == 1
-
res << "\\" * (escapes - 1) << '#{'
-
else
-
res << "\\" * [0, escapes - 1].max
-
res << Script::Parser.new(
-
scan, line, offset + scan.pos - scan.matched_size, options).
-
parse_interpolated
-
end
-
end
-
res << rest
-
end
-
end
-
end
-
1
require 'set'
-
-
1
module Sass
-
# The lexical environment for SassScript.
-
# This keeps track of variable, mixin, and function definitions.
-
#
-
# A new environment is created for each level of Sass nesting.
-
# This allows variables to be lexically scoped.
-
# The new environment refers to the environment in the upper scope,
-
# so it has access to variables defined in enclosing scopes,
-
# but new variables are defined locally.
-
#
-
# Environment also keeps track of the {Engine} options
-
# so that they can be made available to {Sass::Script::Functions}.
-
1
class Environment
-
# The enclosing environment,
-
# or nil if this is the global environment.
-
#
-
# @return [Environment]
-
1
attr_reader :parent
-
1
attr_writer :options
-
-
# @param parent [Environment] See \{#parent}
-
1
def initialize(parent = nil)
-
@parent = parent
-
unless parent
-
@stack = []
-
@mixins_in_use = Set.new
-
@files_in_use = Set.new
-
end
-
end
-
-
# The options hash.
-
# See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
-
#
-
# @return [{Symbol => Object}]
-
1
def options
-
@options || parent_options || {}
-
end
-
-
# Push a new stack frame onto the mixin/include stack.
-
#
-
# @param frame_info [{Symbol => Object}]
-
# Frame information has the following keys:
-
#
-
# `:filename`
-
# : The name of the file in which the lexical scope changed.
-
#
-
# `:mixin`
-
# : The name of the mixin in which the lexical scope changed,
-
# or `nil` if it wasn't within in a mixin.
-
#
-
# `:line`
-
# : The line of the file on which the lexical scope changed. Never nil.
-
1
def push_frame(frame_info)
-
top_of_stack = stack.last
-
if top_of_stack && top_of_stack.delete(:prepared)
-
top_of_stack.merge!(frame_info)
-
else
-
stack.push(top_of_stack = frame_info)
-
end
-
mixins_in_use << top_of_stack[:mixin] if top_of_stack[:mixin]
-
files_in_use << top_of_stack[:filename] if top_of_stack[:filename]
-
end
-
-
# Like \{#push\_frame}, but next time a stack frame is pushed,
-
# it will be merged with this frame.
-
#
-
# @param frame_info [{Symbol => Object}] Same as for \{#push\_frame}.
-
1
def prepare_frame(frame_info)
-
push_frame(frame_info.merge(:prepared => true))
-
end
-
-
# Pop a stack frame from the mixin/include stack.
-
1
def pop_frame
-
pop_and_unuse if stack.last && stack.last[:prepared]
-
pop_and_unuse
-
end
-
-
# A list of stack frames in the mixin/include stack.
-
# The last element in the list is the most deeply-nested frame.
-
#
-
# @return [Array<{Symbol => Object}>] The stack frames,
-
# of the form passed to \{#push\_frame}.
-
1
def stack
-
@stack ||= @parent.stack
-
end
-
-
# A set of names of mixins currently present in the stack.
-
#
-
# @return [Set<String>] The mixin names.
-
1
def mixins_in_use
-
@mixins_in_use ||= @parent.mixins_in_use
-
end
-
-
# A set of names of files currently present in the stack.
-
#
-
# @return [Set<String>] The filenames.
-
1
def files_in_use
-
@files_in_use ||= @parent.files_in_use
-
end
-
-
1
def stack_trace
-
trace = []
-
stack.reverse.each_with_index do |entry, i|
-
msg = "#{i == 0 ? "on" : "from"} line #{entry[:line]}"
-
msg << " of #{entry[:filename] || "an unknown file"}"
-
msg << ", in `#{entry[:mixin]}'" if entry[:mixin]
-
trace << msg
-
end
-
trace
-
end
-
-
1
private
-
-
1
def pop_and_unuse
-
popped = stack.pop
-
mixins_in_use.delete(popped[:mixin]) if popped && popped[:mixin]
-
files_in_use.delete(popped[:filename]) if popped && popped[:filename]
-
popped
-
end
-
-
1
def parent_options
-
@parent_options ||= @parent && @parent.options
-
end
-
-
1
class << self
-
1
private
-
1
UNDERSCORE, DASH = '_', '-'
-
-
# Note: when updating this,
-
# update sass/yard/inherited_hash.rb as well.
-
1
def inherited_hash(name)
-
3
class_eval <<RUBY, __FILE__, __LINE__ + 1
-
def #{name}(name)
-
_#{name}(name.tr(UNDERSCORE, DASH))
-
end
-
-
def _#{name}(name)
-
(@#{name}s && @#{name}s[name]) || @parent && @parent._#{name}(name)
-
end
-
protected :_#{name}
-
-
def set_#{name}(name, value)
-
name = name.tr(UNDERSCORE, DASH)
-
@#{name}s[name] = value unless try_set_#{name}(name, value)
-
end
-
-
def try_set_#{name}(name, value)
-
@#{name}s ||= {}
-
if @#{name}s.include?(name)
-
@#{name}s[name] = value
-
true
-
elsif @parent
-
@parent.try_set_#{name}(name, value)
-
else
-
false
-
end
-
end
-
protected :try_set_#{name}
-
-
def set_local_#{name}(name, value)
-
@#{name}s ||= {}
-
@#{name}s[name.tr(UNDERSCORE, DASH)] = value
-
end
-
RUBY
-
end
-
end
-
-
# variable
-
# Script::Literal
-
1
inherited_hash :var
-
# mixin
-
# Sass::Callable
-
1
inherited_hash :mixin
-
# function
-
# Sass::Callable
-
1
inherited_hash :function
-
end
-
end
-
1
module Sass
-
# An exception class that keeps track of
-
# the line of the Sass template it was raised on
-
# and the Sass file that was being parsed (if applicable).
-
#
-
# All Sass errors are raised as {Sass::SyntaxError}s.
-
#
-
# When dealing with SyntaxErrors,
-
# it's important to provide filename and line number information.
-
# This will be used in various error reports to users, including backtraces;
-
# see \{#sass\_backtrace} for details.
-
#
-
# Some of this information is usually provided as part of the constructor.
-
# New backtrace entries can be added with \{#add\_backtrace},
-
# which is called when an exception is raised between files (e.g. with `@import`).
-
#
-
# Often, a chunk of code will all have similar backtrace information -
-
# the same filename or even line.
-
# It may also be useful to have a default line number set.
-
# In those situations, the default values can be used
-
# by omitting the information on the original exception,
-
# and then calling \{#modify\_backtrace} in a wrapper `rescue`.
-
# When doing this, be sure that all exceptions ultimately end up
-
# with the information filled in.
-
1
class SyntaxError < StandardError
-
# The backtrace of the error within Sass files.
-
# This is an array of hashes containing information for a single entry.
-
# The hashes have the following keys:
-
#
-
# `:filename`
-
# : The name of the file in which the exception was raised,
-
# or `nil` if no filename is available.
-
#
-
# `:mixin`
-
# : The name of the mixin in which the exception was raised,
-
# or `nil` if it wasn't raised in a mixin.
-
#
-
# `:line`
-
# : The line of the file on which the error occurred. Never nil.
-
#
-
# This information is also included in standard backtrace format
-
# in the output of \{#backtrace}.
-
#
-
# @return [Aray<{Symbol => Object>}]
-
1
attr_accessor :sass_backtrace
-
-
# The text of the template where this error was raised.
-
#
-
# @return [String]
-
1
attr_accessor :sass_template
-
-
# @param msg [String] The error message
-
# @param attrs [{Symbol => Object}] The information in the backtrace entry.
-
# See \{#sass\_backtrace}
-
1
def initialize(msg, attrs = {})
-
@message = msg
-
@sass_backtrace = []
-
add_backtrace(attrs)
-
end
-
-
# The name of the file in which the exception was raised.
-
# This could be `nil` if no filename is available.
-
#
-
# @return [String, nil]
-
1
def sass_filename
-
sass_backtrace.first[:filename]
-
end
-
-
# The name of the mixin in which the error occurred.
-
# This could be `nil` if the error occurred outside a mixin.
-
#
-
# @return [Fixnum]
-
1
def sass_mixin
-
sass_backtrace.first[:mixin]
-
end
-
-
# The line of the Sass template on which the error occurred.
-
#
-
# @return [Fixnum]
-
1
def sass_line
-
sass_backtrace.first[:line]
-
end
-
-
# Adds an entry to the exception's Sass backtrace.
-
#
-
# @param attrs [{Symbol => Object}] The information in the backtrace entry.
-
# See \{#sass\_backtrace}
-
1
def add_backtrace(attrs)
-
sass_backtrace << attrs.reject {|k, v| v.nil?}
-
end
-
-
# Modify the top Sass backtrace entries
-
# (that is, the most deeply nested ones)
-
# to have the given attributes.
-
#
-
# Specifically, this goes through the backtrace entries
-
# from most deeply nested to least,
-
# setting the given attributes for each entry.
-
# If an entry already has one of the given attributes set,
-
# the pre-existing attribute takes precedence
-
# and is not used for less deeply-nested entries
-
# (even if they don't have that attribute set).
-
#
-
# @param attrs [{Symbol => Object}] The information to add to the backtrace entry.
-
# See \{#sass\_backtrace}
-
1
def modify_backtrace(attrs)
-
attrs = attrs.reject {|k, v| v.nil?}
-
# Move backwards through the backtrace
-
(0...sass_backtrace.size).to_a.reverse.each do |i|
-
entry = sass_backtrace[i]
-
sass_backtrace[i] = attrs.merge(entry)
-
attrs.reject! {|k, v| entry.include?(k)}
-
break if attrs.empty?
-
end
-
end
-
-
# @return [String] The error message
-
1
def to_s
-
@message
-
end
-
-
# Returns the standard exception backtrace,
-
# including the Sass backtrace.
-
#
-
# @return [Array<String>]
-
1
def backtrace
-
return nil if super.nil?
-
return super if sass_backtrace.all? {|h| h.empty?}
-
sass_backtrace.map do |h|
-
"#{h[:filename] || "(sass)"}:#{h[:line]}" +
-
(h[:mixin] ? ":in `#{h[:mixin]}'" : "")
-
end + super
-
end
-
-
# Returns a string representation of the Sass backtrace.
-
#
-
# @param default_filename [String] The filename to use for unknown files
-
# @see #sass_backtrace
-
# @return [String]
-
1
def sass_backtrace_str(default_filename = "an unknown file")
-
lines = self.message.split("\n")
-
msg = lines[0] + lines[1..-1].
-
map {|l| "\n" + (" " * "Syntax error: ".size) + l}.join
-
"Syntax error: #{msg}" +
-
Sass::Util.enum_with_index(sass_backtrace).map do |entry, i|
-
"\n #{i == 0 ? "on" : "from"} line #{entry[:line]}" +
-
" of #{entry[:filename] || default_filename}" +
-
(entry[:mixin] ? ", in `#{entry[:mixin]}'" : "")
-
end.join
-
end
-
-
1
class << self
-
# Returns an error report for an exception in CSS format.
-
#
-
# @param e [Exception]
-
# @param options [{Symbol => Object}] The options passed to {Sass::Engine#initialize}
-
# @return [String] The error report
-
# @raise [Exception] `e`, if the
-
# {file:SASS_REFERENCE.md#full_exception-option `:full_exception`} option
-
# is set to false.
-
1
def exception_to_css(e, options)
-
raise e unless options[:full_exception]
-
-
header = header_string(e, options)
-
-
<<END
-
/*
-
#{header}
-
-
Backtrace:\n#{e.backtrace.join("\n")}
-
*/
-
body:before {
-
white-space: pre;
-
font-family: monospace;
-
content: "#{header.gsub('"', '\"').gsub("\n", '\\A ')}"; }
-
END
-
end
-
-
1
private
-
-
1
def header_string(e, options)
-
unless e.is_a?(Sass::SyntaxError) && e.sass_line && e.sass_template
-
return "#{e.class}: #{e.message}"
-
end
-
-
line_offset = options[:line] || 1
-
line_num = e.sass_line + 1 - line_offset
-
min = [line_num - 6, 0].max
-
section = e.sass_template.rstrip.split("\n")[min ... line_num + 5]
-
return e.sass_backtrace_str if section.nil? || section.empty?
-
-
e.sass_backtrace_str + "\n\n" + Sass::Util.enum_with_index(section).
-
map {|line, i| "#{line_offset + min + i}: #{line}"}.join("\n")
-
end
-
end
-
end
-
-
# The class for Sass errors that are raised due to invalid unit conversions
-
# in SassScript.
-
1
class UnitConversionError < SyntaxError; end
-
end
-
1
module Sass
-
# Sass importers are in charge of taking paths passed to `@import`
-
# and finding the appropriate Sass code for those paths.
-
# By default, this code is always loaded from the filesystem,
-
# but importers could be added to load from a database or over HTTP.
-
#
-
# Each importer is in charge of a single load path
-
# (or whatever the corresponding notion is for the backend).
-
# Importers can be placed in the {file:SASS_REFERENCE.md#load_paths-option `:load_paths` array}
-
# alongside normal filesystem paths.
-
#
-
# When resolving an `@import`, Sass will go through the load paths
-
# looking for an importer that successfully imports the path.
-
# Once one is found, the imported file is used.
-
#
-
# User-created importers must inherit from {Importers::Base}.
-
1
module Importers
-
end
-
end
-
-
1
require 'sass/importers/base'
-
1
require 'sass/importers/filesystem'
-
1
module Sass
-
1
module Importers
-
# The abstract base class for Sass importers.
-
# All importers should inherit from this.
-
#
-
# At the most basic level, an importer is given a string
-
# and must return a {Sass::Engine} containing some Sass code.
-
# This string can be interpreted however the importer wants;
-
# however, subclasses are encouraged to use the URI format
-
# for pathnames.
-
#
-
# Importers that have some notion of "relative imports"
-
# should take a single load path in their constructor,
-
# and interpret paths as relative to that.
-
# They should also implement the \{#find\_relative} method.
-
#
-
# Importers should be serializable via `Marshal.dump`.
-
# In addition to the standard `_dump` and `_load` methods,
-
# importers can define `_before_dump`, `_after_dump`, `_around_dump`,
-
# and `_after_load` methods as per {Sass::Util#dump} and {Sass::Util#load}.
-
#
-
# @abstract
-
1
class Base
-
-
# Find a Sass file relative to another file.
-
# Importers without a notion of "relative paths"
-
# should just return nil here.
-
#
-
# If the importer does have a notion of "relative paths",
-
# it should ignore its load path during this method.
-
#
-
# See \{#find} for important information on how this method should behave.
-
#
-
# The `:filename` option passed to the returned {Sass::Engine}
-
# should be of a format that could be passed to \{#find}.
-
#
-
# @param uri [String] The URI to import. This is not necessarily relative,
-
# but this method should only return true if it is.
-
# @param base [String] The base filename. If `uri` is relative,
-
# it should be interpreted as relative to `base`.
-
# `base` is guaranteed to be in a format importable by this importer.
-
# @param options [{Symbol => Object}] Options for the Sass file
-
# containing the `@import` that's currently being resolved.
-
# @return [Sass::Engine, nil] An Engine containing the imported file,
-
# or nil if it couldn't be found or was in the wrong format.
-
1
def find_relative(uri, base, options)
-
Sass::Util.abstract(self)
-
end
-
-
# Find a Sass file, if it exists.
-
#
-
# This is the primary entry point of the Importer.
-
# It corresponds directly to an `@import` statement in Sass.
-
# It should do three basic things:
-
#
-
# * Determine if the URI is in this importer's format.
-
# If not, return nil.
-
# * Determine if the file indicated by the URI actually exists and is readable.
-
# If not, return nil.
-
# * Read the file and place the contents in a {Sass::Engine}.
-
# Return that engine.
-
#
-
# If this importer's format allows for file extensions,
-
# it should treat them the same way as the default {Filesystem} importer.
-
# If the URI explicitly has a `.sass` or `.scss` filename,
-
# the importer should look for that exact file
-
# and import it as the syntax indicated.
-
# If it doesn't exist, the importer should return nil.
-
#
-
# If the URI doesn't have either of these extensions,
-
# the importer should look for files with the extensions.
-
# If no such files exist, it should return nil.
-
#
-
# The {Sass::Engine} to be returned should be passed `options`,
-
# with a few modifications. `:syntax` should be set appropriately,
-
# `:filename` should be set to `uri`,
-
# and `:importer` should be set to this importer.
-
#
-
# @param uri [String] The URI to import.
-
# @param options [{Symbol => Object}] Options for the Sass file
-
# containing the `@import` that's currently being resolved.
-
# This is safe for subclasses to modify destructively.
-
# Callers should only pass in a value they don't mind being destructively modified.
-
# @return [Sass::Engine, nil] An Engine containing the imported file,
-
# or nil if it couldn't be found or was in the wrong format.
-
1
def find(uri, options)
-
Sass::Util.abstract(self)
-
end
-
-
# Returns the time the given Sass file was last modified.
-
#
-
# If the given file has been deleted or the time can't be accessed
-
# for some other reason, this should return nil.
-
#
-
# @param uri [String] The URI of the file to check.
-
# Comes from a `:filename` option set on an engine returned by this importer.
-
# @param options [{Symbol => Objet}] Options for the Sass file
-
# containing the `@import` currently being checked.
-
# @return [Time, nil]
-
1
def mtime(uri, options)
-
Sass::Util.abstract(self)
-
end
-
-
# Get the cache key pair for the given Sass URI.
-
# The URI need not be checked for validity.
-
#
-
# The only strict requirement is that the returned pair of strings
-
# uniquely identify the file at the given URI.
-
# However, the first component generally corresponds roughly to the directory,
-
# and the second to the basename, of the URI.
-
#
-
# Note that keys must be unique *across importers*.
-
# Thus it's probably a good idea to include the importer name
-
# at the beginning of the first component.
-
#
-
# @param uri [String] A URI known to be valid for this importer.
-
# @param options [{Symbol => Object}] Options for the Sass file
-
# containing the `@import` currently being checked.
-
# @return [(String, String)] The key pair which uniquely identifies
-
# the file at the given URI.
-
1
def key(uri, options)
-
Sass::Util.abstract(self)
-
end
-
-
# A string representation of the importer.
-
# Should be overridden by subclasses.
-
#
-
# This is used to help debugging,
-
# and should usually just show the load path encapsulated by this importer.
-
#
-
# @return [String]
-
1
def to_s
-
Sass::Util.abstract(self)
-
end
-
end
-
end
-
end
-
-
-
1
require 'pathname'
-
-
1
module Sass
-
1
module Importers
-
# The default importer, used for any strings found in the load path.
-
# Simply loads Sass files from the filesystem using the default logic.
-
1
class Filesystem < Base
-
-
1
attr_accessor :root
-
-
# Creates a new filesystem importer that imports files relative to a given path.
-
#
-
# @param root [String] The root path.
-
# This importer will import files relative to this path.
-
1
def initialize(root)
-
@root = File.expand_path(root)
-
end
-
-
# @see Base#find_relative
-
1
def find_relative(name, base, options)
-
_find(File.dirname(base), name, options)
-
end
-
-
# @see Base#find
-
1
def find(name, options)
-
_find(@root, name, options)
-
end
-
-
# @see Base#mtime
-
1
def mtime(name, options)
-
file, s = find_real_file(@root, name)
-
File.mtime(file) if file
-
rescue Errno::ENOENT
-
nil
-
end
-
-
# @see Base#key
-
1
def key(name, options)
-
[self.class.name + ":" + File.dirname(File.expand_path(name)),
-
File.basename(name)]
-
end
-
-
# @see Base#to_s
-
1
def to_s
-
@root
-
end
-
-
1
def hash
-
@root.hash
-
end
-
-
1
def eql?(other)
-
root.eql?(other.root)
-
end
-
-
1
protected
-
-
# If a full uri is passed, this removes the root from it
-
# otherwise returns the name unchanged
-
1
def remove_root(name)
-
if name.index(@root + "/") == 0
-
name[(@root.length + 1)..-1]
-
else
-
name
-
end
-
end
-
-
# A hash from file extensions to the syntaxes for those extensions.
-
# The syntaxes must be `:sass` or `:scss`.
-
#
-
# This can be overridden by subclasses that want normal filesystem importing
-
# with unusual extensions.
-
#
-
# @return [{String => Symbol}]
-
1
def extensions
-
{'sass' => :sass, 'scss' => :scss}
-
end
-
-
# Given an `@import`ed path, returns an array of possible
-
# on-disk filenames and their corresponding syntaxes for that path.
-
#
-
# @param name [String] The filename.
-
# @return [Array(String, Symbol)] An array of pairs.
-
# The first element of each pair is a filename to look for;
-
# the second element is the syntax that file would be in (`:sass` or `:scss`).
-
1
def possible_files(name)
-
name = escape_glob_characters(name)
-
dirname, basename, extname = split(name)
-
sorted_exts = extensions.sort
-
syntax = extensions[extname]
-
-
return [["#{dirname}/{_,}#{basename}.#{extensions.invert[syntax]}", syntax]] if syntax
-
sorted_exts.map {|ext, syn| ["#{dirname}/{_,}#{basename}.#{ext}", syn]}
-
end
-
-
1
def escape_glob_characters(name)
-
name.gsub(/[\*\[\]\{\}\?]/) do |char|
-
"\\#{char}"
-
end
-
end
-
-
1
REDUNDANT_DIRECTORY = %r{#{Regexp.escape(File::SEPARATOR)}\.#{Regexp.escape(File::SEPARATOR)}}
-
# Given a base directory and an `@import`ed name,
-
# finds an existant file that matches the name.
-
#
-
# @param dir [String] The directory relative to which to search.
-
# @param name [String] The filename to search for.
-
# @return [(String, Symbol)] A filename-syntax pair.
-
1
def find_real_file(dir, name)
-
for (f,s) in possible_files(remove_root(name))
-
path = (dir == "." || Pathname.new(f).absolute?) ? f : "#{dir}/#{f}"
-
if full_path = Dir[path].first
-
full_path.gsub!(REDUNDANT_DIRECTORY,File::SEPARATOR)
-
return full_path, s
-
end
-
end
-
nil
-
end
-
-
# Splits a filename into three parts, a directory part, a basename, and an extension
-
# Only the known extensions returned from the extensions method will be recognized as such.
-
1
def split(name)
-
extension = nil
-
dirname, basename = File.dirname(name), File.basename(name)
-
if basename =~ /^(.*)\.(#{extensions.keys.map{|e| Regexp.escape(e)}.join('|')})$/
-
basename = $1
-
extension = $2
-
end
-
[dirname, basename, extension]
-
end
-
-
1
private
-
-
1
def _find(dir, name, options)
-
full_filename, syntax = find_real_file(dir, name)
-
return unless full_filename && File.readable?(full_filename)
-
-
options[:syntax] = syntax
-
options[:filename] = full_filename
-
options[:importer] = self
-
Sass::Engine.new(File.read(full_filename), options)
-
end
-
-
1
def join(base, path)
-
Pathname.new(base).join(path).to_s
-
end
-
end
-
end
-
end
-
1
module Sass::Logger
-
-
end
-
-
1
require "sass/logger/log_level"
-
1
require "sass/logger/base"
-
-
1
module Sass
-
-
1
class << self
-
1
attr_accessor :logger
-
end
-
-
1
self.logger = Sass::Logger::Base.new
-
end
-
1
require 'sass/logger/log_level'
-
-
1
class Sass::Logger::Base
-
-
1
include Sass::Logger::LogLevel
-
-
1
attr_accessor :log_level
-
1
attr_accessor :disabled
-
-
1
log_level :trace
-
1
log_level :debug
-
1
log_level :info
-
1
log_level :warn
-
1
log_level :error
-
-
1
def initialize(log_level = :debug)
-
2
self.log_level = log_level
-
end
-
-
1
def logging_level?(level)
-
!disabled && self.class.log_level?(level, log_level)
-
end
-
-
1
def log(level, message)
-
self._log(level, message) if logging_level?(level)
-
end
-
-
1
def _log(level, message)
-
Kernel::warn(message)
-
end
-
-
end
-
1
module Sass
-
1
module Logger
-
1
module LogLevel
-
-
1
def self.included(base)
-
1
base.extend(ClassMethods)
-
end
-
-
1
module ClassMethods
-
1
def inherited(subclass)
-
1
subclass.log_levels = subclass.superclass.log_levels.dup
-
end
-
-
1
def log_levels
-
11
@log_levels ||= {}
-
end
-
-
1
def log_levels=(levels)
-
1
@log_levels = levels
-
end
-
-
1
def log_level?(level, min_level)
-
log_levels[level] >= log_levels[min_level]
-
end
-
-
1
def log_level(name, options = {})
-
5
if options[:prepend]
-
level = log_levels.values.min
-
level = level.nil? ? 0 : level - 1
-
else
-
5
level = log_levels.values.max
-
5
level = level.nil? ? 0 : level + 1
-
end
-
5
log_levels.update(name => level)
-
5
define_logger(name)
-
end
-
-
1
def define_logger(name, options = {})
-
class_eval %Q{
-
def #{name}(message)
-
#{options.fetch(:to, :log)}(#{name.inspect}, message)
-
end
-
5
}
-
end
-
end
-
-
end
-
end
-
end
-
# Rails 3.0.0.beta.2+, < 3.1
-
1
if defined?(ActiveSupport) && Sass::Util.has?(:public_method, ActiveSupport, :on_load) &&
-
!Sass::Util.ap_geq?('3.1.0.beta')
-
require 'sass/plugin/configuration'
-
ActiveSupport.on_load(:before_configuration) do
-
require 'sass'
-
require 'sass/plugin'
-
end
-
end
-
1
module Sass
-
# The root directory of the Sass source tree.
-
# This may be overridden by the package manager
-
# if the lib directory is separated from the main source tree.
-
# @api public
-
1
ROOT_DIR = File.expand_path(File.join(__FILE__, "../../.."))
-
end
-
1
require 'sass/script/node'
-
1
require 'sass/script/variable'
-
1
require 'sass/script/funcall'
-
1
require 'sass/script/operation'
-
1
require 'sass/script/literal'
-
1
require 'sass/script/parser'
-
-
1
module Sass
-
# SassScript is code that's embedded in Sass documents
-
# to allow for property values to be computed from variables.
-
#
-
# This module contains code that handles the parsing and evaluation of SassScript.
-
1
module Script
-
# The regular expression used to parse variables.
-
1
MATCH = /^\$(#{Sass::SCSS::RX::IDENT})\s*:\s*(.+?)(!(?i:default))?$/
-
-
# The regular expression used to validate variables without matching.
-
1
VALIDATE = /^\$#{Sass::SCSS::RX::IDENT}$/
-
-
# Parses a string of SassScript
-
#
-
# @param value [String] The SassScript
-
# @param line [Fixnum] The number of the line on which the SassScript appeared.
-
# Used for error reporting
-
# @param offset [Fixnum] The number of characters in on `line` that the SassScript started.
-
# Used for error reporting
-
# @param options [{Symbol => Object}] An options hash;
-
# see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
-
# @return [Script::Node] The root node of the parse tree
-
1
def self.parse(value, line, offset, options = {})
-
Parser.parse(value, line, offset, options)
-
rescue Sass::SyntaxError => e
-
e.message << ": #{value.inspect}." if e.message == "SassScript error"
-
e.modify_backtrace(:line => line, :filename => options[:filename])
-
raise e
-
end
-
-
end
-
end
-
1
require 'sass/script/literal'
-
-
1
module Sass::Script
-
# A SassScript object representing a boolean (true or false) value.
-
1
class Bool < Literal
-
# The Ruby value of the boolean.
-
#
-
# @return [Boolean]
-
1
attr_reader :value
-
1
alias_method :to_bool, :value
-
-
# @return [String] "true" or "false"
-
1
def to_s(opts = {})
-
@value.to_s
-
end
-
1
alias_method :to_sass, :to_s
-
end
-
end
-
1
require 'sass/script/literal'
-
-
1
module Sass::Script
-
# A SassScript object representing a CSS color.
-
#
-
# A color may be represented internally as RGBA, HSLA, or both.
-
# It's originally represented as whatever its input is;
-
# if it's created with RGB values, it's represented as RGBA,
-
# and if it's created with HSL values, it's represented as HSLA.
-
# Once a property is accessed that requires the other representation --
-
# for example, \{#red} for an HSL color --
-
# that component is calculated and cached.
-
#
-
# The alpha channel of a color is independent of its RGB or HSL representation.
-
# It's always stored, as 1 if nothing else is specified.
-
# If only the alpha channel is modified using \{#with},
-
# the cached RGB and HSL values are retained.
-
1
class Color < Literal
-
2
class << self; include Sass::Util; end
-
-
# A hash from color names to `[red, green, blue]` value arrays.
-
1
HTML4_COLORS = map_vals({
-
'black' => 0x000000,
-
'silver' => 0xc0c0c0,
-
'gray' => 0x808080,
-
'white' => 0xffffff,
-
'maroon' => 0x800000,
-
'red' => 0xff0000,
-
'purple' => 0x800080,
-
'fuchsia' => 0xff00ff,
-
'green' => 0x008000,
-
'lime' => 0x00ff00,
-
'olive' => 0x808000,
-
'yellow' => 0xffff00,
-
'navy' => 0x000080,
-
'blue' => 0x0000ff,
-
'teal' => 0x008080,
-
'aqua' => 0x00ffff
-
64
}) {|color| (0..2).map {|n| color >> (n << 3) & 0xff}.reverse}
-
# A hash from `[red, green, blue]` value arrays to color names.
-
17
HTML4_COLORS_REVERSE = map_hash(HTML4_COLORS) {|k, v| [v, k]}
-
-
# Constructs an RGB or HSL color object,
-
# optionally with an alpha channel.
-
#
-
# The RGB values must be between 0 and 255.
-
# The saturation and lightness values must be between 0 and 100.
-
# The alpha value must be between 0 and 1.
-
#
-
# @raise [Sass::SyntaxError] if any color value isn't in the specified range
-
#
-
# @overload initialize(attrs)
-
# The attributes are specified as a hash.
-
# This hash must contain either `:hue`, `:saturation`, and `:value` keys,
-
# or `:red`, `:green`, and `:blue` keys.
-
# It cannot contain both HSL and RGB keys.
-
# It may also optionally contain an `:alpha` key.
-
#
-
# @param attrs [{Symbol => Numeric}] A hash of color attributes to values
-
# @raise [ArgumentError] if not enough attributes are specified,
-
# or both RGB and HSL attributes are specified
-
#
-
# @overload initialize(rgba)
-
# The attributes are specified as an array.
-
# This overload only supports RGB or RGBA colors.
-
#
-
# @param rgba [Array<Numeric>] A three- or four-element array
-
# of the red, green, blue, and optionally alpha values (respectively)
-
# of the color
-
# @raise [ArgumentError] if not enough attributes are specified
-
1
def initialize(attrs, allow_both_rgb_and_hsl = false)
-
super(nil)
-
-
if attrs.is_a?(Array)
-
unless (3..4).include?(attrs.size)
-
raise ArgumentError.new("Color.new(array) expects a three- or four-element array")
-
end
-
-
red, green, blue = attrs[0...3].map {|c| c.to_i}
-
@attrs = {:red => red, :green => green, :blue => blue}
-
@attrs[:alpha] = attrs[3] ? attrs[3].to_f : 1
-
else
-
attrs = attrs.reject {|k, v| v.nil?}
-
hsl = [:hue, :saturation, :lightness] & attrs.keys
-
rgb = [:red, :green, :blue] & attrs.keys
-
if !allow_both_rgb_and_hsl && !hsl.empty? && !rgb.empty?
-
raise ArgumentError.new("Color.new(hash) may not have both HSL and RGB keys specified")
-
elsif hsl.empty? && rgb.empty?
-
raise ArgumentError.new("Color.new(hash) must have either HSL or RGB keys specified")
-
elsif !hsl.empty? && hsl.size != 3
-
raise ArgumentError.new("Color.new(hash) must have all three HSL values specified")
-
elsif !rgb.empty? && rgb.size != 3
-
raise ArgumentError.new("Color.new(hash) must have all three RGB values specified")
-
end
-
-
@attrs = attrs
-
@attrs[:hue] %= 360 if @attrs[:hue]
-
@attrs[:alpha] ||= 1
-
end
-
-
[:red, :green, :blue].each do |k|
-
next if @attrs[k].nil?
-
@attrs[k] = @attrs[k].to_i
-
next if (0..255).include?(@attrs[k])
-
raise ArgumentError.new("#{k.to_s.capitalize} value must be between 0 and 255")
-
end
-
-
[:saturation, :lightness].each do |k|
-
next if @attrs[k].nil?
-
@attrs[k] = 0 if @attrs[k] < 0.00001 && @attrs[k] > -0.00001
-
@attrs[k] = 100 if @attrs[k] - 100 < 0.00001 && @attrs[k] - 100 > -0.00001
-
next if (0..100).include?(@attrs[k])
-
raise ArgumentError.new("#{k.to_s.capitalize} must be between 0 and 100")
-
end
-
-
unless (0..1).include?(@attrs[:alpha])
-
raise ArgumentError.new("Alpha channel must be between 0 and 1")
-
end
-
end
-
-
# The red component of the color.
-
#
-
# @return [Fixnum]
-
1
def red
-
hsl_to_rgb!
-
@attrs[:red]
-
end
-
-
# The green component of the color.
-
#
-
# @return [Fixnum]
-
1
def green
-
hsl_to_rgb!
-
@attrs[:green]
-
end
-
-
# The blue component of the color.
-
#
-
# @return [Fixnum]
-
1
def blue
-
hsl_to_rgb!
-
@attrs[:blue]
-
end
-
-
# The hue component of the color.
-
#
-
# @return [Numeric]
-
1
def hue
-
rgb_to_hsl!
-
@attrs[:hue]
-
end
-
-
# The saturation component of the color.
-
#
-
# @return [Numeric]
-
1
def saturation
-
rgb_to_hsl!
-
@attrs[:saturation]
-
end
-
-
# The lightness component of the color.
-
#
-
# @return [Numeric]
-
1
def lightness
-
rgb_to_hsl!
-
@attrs[:lightness]
-
end
-
-
# The alpha channel (opacity) of the color.
-
# This is 1 unless otherwise defined.
-
#
-
# @return [Fixnum]
-
1
def alpha
-
@attrs[:alpha]
-
end
-
-
# Returns whether this color object is translucent;
-
# that is, whether the alpha channel is non-1.
-
#
-
# @return [Boolean]
-
1
def alpha?
-
alpha < 1
-
end
-
-
# Returns the red, green, and blue components of the color.
-
#
-
# @return [Array<Fixnum>] A frozen three-element array of the red, green, and blue
-
# values (respectively) of the color
-
1
def rgb
-
[red, green, blue].freeze
-
end
-
-
# Returns the hue, saturation, and lightness components of the color.
-
#
-
# @return [Array<Fixnum>] A frozen three-element array of the
-
# hue, saturation, and lightness values (respectively) of the color
-
1
def hsl
-
[hue, saturation, lightness].freeze
-
end
-
-
# The SassScript `==` operation.
-
# **Note that this returns a {Sass::Script::Bool} object,
-
# not a Ruby boolean**.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Bool] True if this literal is the same as the other,
-
# false otherwise
-
1
def eq(other)
-
Sass::Script::Bool.new(
-
other.is_a?(Color) && rgb == other.rgb && alpha == other.alpha)
-
end
-
-
# Returns a copy of this color with one or more channels changed.
-
# RGB or HSL colors may be changed, but not both at once.
-
#
-
# For example:
-
#
-
# Color.new([10, 20, 30]).with(:blue => 40)
-
# #=> rgb(10, 40, 30)
-
# Color.new([126, 126, 126]).with(:red => 0, :green => 255)
-
# #=> rgb(0, 255, 126)
-
# Color.new([255, 0, 127]).with(:saturation => 60)
-
# #=> rgb(204, 51, 127)
-
# Color.new([1, 2, 3]).with(:alpha => 0.4)
-
# #=> rgba(1, 2, 3, 0.4)
-
#
-
# @param attrs [{Symbol => Numeric}]
-
# A map of channel names (`:red`, `:green`, `:blue`,
-
# `:hue`, `:saturation`, `:lightness`, or `:alpha`) to values
-
# @return [Color] The new Color object
-
# @raise [ArgumentError] if both RGB and HSL keys are specified
-
1
def with(attrs)
-
attrs = attrs.reject {|k, v| v.nil?}
-
hsl = !([:hue, :saturation, :lightness] & attrs.keys).empty?
-
rgb = !([:red, :green, :blue] & attrs.keys).empty?
-
if hsl && rgb
-
raise ArgumentError.new("Cannot specify HSL and RGB values for a color at the same time")
-
end
-
-
if hsl
-
[:hue, :saturation, :lightness].each {|k| attrs[k] ||= send(k)}
-
elsif rgb
-
[:red, :green, :blue].each {|k| attrs[k] ||= send(k)}
-
else
-
# If we're just changing the alpha channel,
-
# keep all the HSL/RGB stuff we've calculated
-
attrs = @attrs.merge(attrs)
-
end
-
attrs[:alpha] ||= alpha
-
-
Color.new(attrs, :allow_both_rgb_and_hsl)
-
end
-
-
# The SassScript `+` operation.
-
# Its functionality depends on the type of its argument:
-
#
-
# {Number}
-
# : Adds the number to each of the RGB color channels.
-
#
-
# {Color}
-
# : Adds each of the RGB color channels together.
-
#
-
# {Literal}
-
# : See {Literal#plus}.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Color] The resulting color
-
# @raise [Sass::SyntaxError] if `other` is a number with units
-
1
def plus(other)
-
if other.is_a?(Sass::Script::Number) || other.is_a?(Sass::Script::Color)
-
piecewise(other, :+)
-
else
-
super
-
end
-
end
-
-
# The SassScript `-` operation.
-
# Its functionality depends on the type of its argument:
-
#
-
# {Number}
-
# : Subtracts the number from each of the RGB color channels.
-
#
-
# {Color}
-
# : Subtracts each of the other color's RGB color channels from this color's.
-
#
-
# {Literal}
-
# : See {Literal#minus}.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Color] The resulting color
-
# @raise [Sass::SyntaxError] if `other` is a number with units
-
1
def minus(other)
-
if other.is_a?(Sass::Script::Number) || other.is_a?(Sass::Script::Color)
-
piecewise(other, :-)
-
else
-
super
-
end
-
end
-
-
# The SassScript `*` operation.
-
# Its functionality depends on the type of its argument:
-
#
-
# {Number}
-
# : Multiplies the number by each of the RGB color channels.
-
#
-
# {Color}
-
# : Multiplies each of the RGB color channels together.
-
#
-
# @param other [Number, Color] The right-hand side of the operator
-
# @return [Color] The resulting color
-
# @raise [Sass::SyntaxError] if `other` is a number with units
-
1
def times(other)
-
if other.is_a?(Sass::Script::Number) || other.is_a?(Sass::Script::Color)
-
piecewise(other, :*)
-
else
-
raise NoMethodError.new(nil, :times)
-
end
-
end
-
-
# The SassScript `/` operation.
-
# Its functionality depends on the type of its argument:
-
#
-
# {Number}
-
# : Divides each of the RGB color channels by the number.
-
#
-
# {Color}
-
# : Divides each of this color's RGB color channels by the other color's.
-
#
-
# {Literal}
-
# : See {Literal#div}.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Color] The resulting color
-
# @raise [Sass::SyntaxError] if `other` is a number with units
-
1
def div(other)
-
if other.is_a?(Sass::Script::Number) || other.is_a?(Sass::Script::Color)
-
piecewise(other, :/)
-
else
-
super
-
end
-
end
-
-
# The SassScript `%` operation.
-
# Its functionality depends on the type of its argument:
-
#
-
# {Number}
-
# : Takes each of the RGB color channels module the number.
-
#
-
# {Color}
-
# : Takes each of this color's RGB color channels modulo the other color's.
-
#
-
# @param other [Number, Color] The right-hand side of the operator
-
# @return [Color] The resulting color
-
# @raise [Sass::SyntaxError] if `other` is a number with units
-
1
def mod(other)
-
if other.is_a?(Sass::Script::Number) || other.is_a?(Sass::Script::Color)
-
piecewise(other, :%)
-
else
-
raise NoMethodError.new(nil, :mod)
-
end
-
end
-
-
# Returns a string representation of the color.
-
# This is usually the color's hex value,
-
# but if the color has a name that's used instead.
-
#
-
# @return [String] The string representation
-
1
def to_s(opts = {})
-
return rgba_str if alpha?
-
return smallest if options[:style] == :compressed
-
return HTML4_COLORS_REVERSE[rgb] if HTML4_COLORS_REVERSE[rgb]
-
hex_str
-
end
-
1
alias_method :to_sass, :to_s
-
-
# Returns a string representation of the color.
-
#
-
# @return [String] The hex value
-
1
def inspect
-
alpha? ? rgba_str : hex_str
-
end
-
-
1
private
-
-
1
def smallest
-
small_hex_str = hex_str.gsub(/^#(.)\1(.)\2(.)\3$/, '#\1\2\3')
-
return small_hex_str unless (color = HTML4_COLORS_REVERSE[rgb]) &&
-
color.size <= small_hex_str.size
-
return color
-
end
-
-
1
def rgba_str
-
split = options[:style] == :compressed ? ',' : ', '
-
"rgba(#{rgb.join(split)}#{split}#{Number.round(alpha)})"
-
end
-
-
1
def hex_str
-
red, green, blue = rgb.map { |num| num.to_s(16).rjust(2, '0') }
-
"##{red}#{green}#{blue}"
-
end
-
-
1
def piecewise(other, operation)
-
other_num = other.is_a? Number
-
if other_num && !other.unitless?
-
raise Sass::SyntaxError.new("Cannot add a number with units (#{other}) to a color (#{self}).")
-
end
-
-
result = []
-
for i in (0...3)
-
res = rgb[i].send(operation, other_num ? other.value : other.rgb[i])
-
result[i] = [ [res, 255].min, 0 ].max
-
end
-
-
if !other_num && other.alpha != alpha
-
raise Sass::SyntaxError.new("Alpha channels must be equal: #{self} #{operation} #{other}")
-
end
-
-
with(:red => result[0], :green => result[1], :blue => result[2])
-
end
-
-
1
def hsl_to_rgb!
-
return if @attrs[:red] && @attrs[:blue] && @attrs[:green]
-
-
h = @attrs[:hue] / 360.0
-
s = @attrs[:saturation] / 100.0
-
l = @attrs[:lightness] / 100.0
-
-
# Algorithm from the CSS3 spec: http://www.w3.org/TR/css3-color/#hsl-color.
-
m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s
-
m1 = l * 2 - m2
-
@attrs[:red], @attrs[:green], @attrs[:blue] = [
-
hue_to_rgb(m1, m2, h + 1.0/3),
-
hue_to_rgb(m1, m2, h),
-
hue_to_rgb(m1, m2, h - 1.0/3)
-
].map {|c| (c * 0xff).round}
-
end
-
-
1
def hue_to_rgb(m1, m2, h)
-
h += 1 if h < 0
-
h -= 1 if h > 1
-
return m1 + (m2 - m1) * h * 6 if h * 6 < 1
-
return m2 if h * 2 < 1
-
return m1 + (m2 - m1) * (2.0/3 - h) * 6 if h * 3 < 2
-
return m1
-
end
-
-
1
def rgb_to_hsl!
-
return if @attrs[:hue] && @attrs[:saturation] && @attrs[:lightness]
-
r, g, b = [:red, :green, :blue].map {|k| @attrs[k] / 255.0}
-
-
# Algorithm from http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
-
max = [r, g, b].max
-
min = [r, g, b].min
-
d = max - min
-
-
h =
-
case max
-
when min; 0
-
when r; 60 * (g-b)/d
-
when g; 60 * (b-r)/d + 120
-
when b; 60 * (r-g)/d + 240
-
end
-
-
l = (max + min)/2.0
-
-
s =
-
if max == min
-
0
-
elsif l < 0.5
-
d/(2*l)
-
else
-
d/(2 - 2*l)
-
end
-
-
@attrs[:hue] = h % 360
-
@attrs[:saturation] = s * 100
-
@attrs[:lightness] = l * 100
-
end
-
end
-
end
-
1
module Sass
-
1
module Script
-
# This is a subclass of {Lexer} for use in parsing plain CSS properties.
-
#
-
# @see Sass::SCSS::CssParser
-
1
class CssLexer < Lexer
-
1
private
-
-
1
def token
-
important || super
-
end
-
-
1
def string(re, *args)
-
if re == :uri
-
return unless uri = scan(URI)
-
return [:string, Script::String.new(uri)]
-
end
-
-
return unless scan(STRING)
-
[:string, Script::String.new((@scanner[1] || @scanner[2]).gsub(/\\(['"])/, '\1'), :string)]
-
end
-
-
1
def important
-
return unless s = scan(IMPORTANT)
-
[:raw, s]
-
end
-
end
-
end
-
end
-
1
require 'sass/script'
-
1
require 'sass/script/css_lexer'
-
-
1
module Sass
-
1
module Script
-
# This is a subclass of {Parser} for use in parsing plain CSS properties.
-
#
-
# @see Sass::SCSS::CssParser
-
1
class CssParser < Parser
-
1
private
-
-
# @private
-
1
def lexer_class; CssLexer; end
-
-
# We need a production that only does /,
-
# since * and % aren't allowed in plain CSS
-
1
production :div, :unary_plus, :div
-
-
1
def string
-
return number unless tok = try_tok(:string)
-
return tok.value unless @lexer.peek && @lexer.peek.type == :begin_interpolation
-
end
-
-
# Short-circuit all the SassScript-only productions
-
1
alias_method :interpolation, :space
-
1
alias_method :or_expr, :div
-
1
alias_method :unary_div, :ident
-
1
alias_method :paren, :string
-
end
-
end
-
end
-
1
require 'sass/script/functions'
-
-
1
module Sass
-
1
module Script
-
# A SassScript parse node representing a function call.
-
#
-
# A function call either calls one of the functions in {Script::Functions},
-
# or if no function with the given name exists
-
# it returns a string representation of the function call.
-
1
class Funcall < Node
-
# The name of the function.
-
#
-
# @return [String]
-
1
attr_reader :name
-
-
# The arguments to the function.
-
#
-
# @return [Array<Script::Node>]
-
1
attr_reader :args
-
-
# The keyword arguments to the function.
-
#
-
# @return [{String => Script::Node}]
-
1
attr_reader :keywords
-
-
# @param name [String] See \{#name}
-
# @param args [Array<Script::Node>] See \{#args}
-
# @param keywords [{String => Script::Node}] See \{#keywords}
-
1
def initialize(name, args, keywords)
-
@name = name
-
@args = args
-
@keywords = keywords
-
super()
-
end
-
-
# @return [String] A string representation of the function call
-
1
def inspect
-
args = @args.map {|a| a.inspect}.join(', ')
-
keywords = Sass::Util.hash_to_a(@keywords).
-
map {|k, v| "$#{k}: #{v.inspect}"}.join(', ')
-
"#{name}(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords})"
-
end
-
-
# @see Node#to_sass
-
1
def to_sass(opts = {})
-
args = @args.map {|a| a.to_sass(opts)}.join(', ')
-
keywords = Sass::Util.hash_to_a(@keywords).
-
map {|k, v| "$#{dasherize(k, opts)}: #{v.to_sass(opts)}"}.join(', ')
-
"#{dasherize(name, opts)}(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords})"
-
end
-
-
# Returns the arguments to the function.
-
#
-
# @return [Array<Node>]
-
# @see Node#children
-
1
def children
-
@args + @keywords.values
-
end
-
-
# @see Node#deep_copy
-
1
def deep_copy
-
node = dup
-
node.instance_variable_set('@args', args.map {|a| a.deep_copy})
-
node.instance_variable_set('@keywords', Hash[keywords.map {|k, v| [k, v.deep_copy]}])
-
node
-
end
-
-
1
protected
-
-
# Evaluates the function call.
-
#
-
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
-
# @return [Literal] The SassScript object that is the value of the function call
-
# @raise [Sass::SyntaxError] if the function call raises an ArgumentError
-
1
def _perform(environment)
-
args = @args.map {|a| a.perform(environment)}
-
if fn = environment.function(@name)
-
keywords = Sass::Util.map_hash(@keywords) {|k, v| [k, v.perform(environment)]}
-
return perform_sass_fn(fn, args, keywords)
-
end
-
-
ruby_name = @name.tr('-', '_')
-
args = construct_ruby_args(ruby_name, args, environment)
-
-
unless Functions.callable?(ruby_name)
-
opts(to_literal(args))
-
else
-
opts(Functions::EvaluationContext.new(environment.options).send(ruby_name, *args))
-
end
-
rescue ArgumentError => e
-
# If this is a legitimate Ruby-raised argument error, re-raise it.
-
# Otherwise, it's an error in the user's stylesheet, so wrap it.
-
if e.message =~ /^wrong number of arguments \(\d+ for \d+\)/ &&
-
e.backtrace[0] !~ /:in `(block in )?#{ruby_name}'$/
-
raise e
-
end
-
raise Sass::SyntaxError.new("#{e.message} for `#{name}'")
-
end
-
-
# This method is factored out from `_perform` so that compass can override
-
# it with a cross-browser implementation for functions that require vendor prefixes
-
# in the generated css.
-
1
def to_literal(args)
-
Script::String.new("#{name}(#{args.join(', ')})")
-
end
-
-
1
private
-
-
1
def construct_ruby_args(name, args, environment)
-
unless signature = Functions.signature(name.to_sym, args.size, @keywords.size)
-
return args if keywords.empty?
-
raise Sass::SyntaxError.new("Function #{name} doesn't support keyword arguments")
-
end
-
keywords = Sass::Util.map_hash(@keywords) {|k, v| [k, v.perform(environment)]}
-
-
# If the user passes more non-keyword args than the function expects,
-
# but it does expect keyword args, Ruby's arg handling won't raise an error.
-
# Since we don't want to make functions think about this,
-
# we'll handle it for them here.
-
if signature.var_kwargs && !signature.var_args && args.size > signature.args.size
-
raise Sass::SyntaxError.new(
-
"#{args[signature.args.size].inspect} is not a keyword argument for `#{name}'")
-
elsif keywords.empty?
-
return args
-
end
-
-
args = args + signature.args[args.size..-1].map do |argname|
-
if keywords.has_key?(argname)
-
keywords.delete(argname)
-
else
-
raise Sass::SyntaxError.new("Function #{name} requires an argument named $#{argname}")
-
end
-
end
-
-
if keywords.size > 0
-
if signature.var_kwargs
-
args << keywords
-
else
-
raise Sass::SyntaxError.new("Function #{name} doesn't take an argument named $#{keywords.keys.sort.first}")
-
end
-
end
-
-
args
-
end
-
-
1
def perform_sass_fn(function, args, keywords)
-
# TODO: merge with mixin arg evaluation?
-
keywords.each do |name, value|
-
# TODO: Make this fast
-
unless function.args.find {|(var, default)| var.underscored_name == name}
-
raise Sass::SyntaxError.new("Function #{@name} doesn't have an argument named $#{name}")
-
end
-
end
-
-
if args.size > function.args.size
-
raise ArgumentError.new("Wrong number of arguments (#{args.size} for #{function.args.size})")
-
end
-
-
environment = function.args.zip(args).
-
inject(Sass::Environment.new(function.environment)) do |env, ((var, default), value)|
-
env.set_local_var(var.name,
-
value || keywords[var.underscored_name] || (default && default.perform(env)))
-
raise Sass::SyntaxError.new("Function #{@name} is missing parameter #{var.inspect}.") unless env.var(var.name)
-
env
-
end
-
-
val = catch :_sass_return do
-
function.tree.each {|c| Sass::Tree::Visitors::Perform.visit(c, environment)}
-
raise Sass::SyntaxError.new("Function #{@name} finished without @return")
-
end
-
val
-
end
-
end
-
end
-
end
-
1
module Sass::Script
-
# Methods in this module are accessible from the SassScript context.
-
# For example, you can write
-
#
-
# $color = hsl(120deg, 100%, 50%)
-
#
-
# and it will call {Sass::Script::Functions#hsl}.
-
#
-
# The following functions are provided:
-
#
-
# *Note: These functions are described in more detail below.*
-
#
-
# ## RGB Functions
-
#
-
# \{#rgb rgb($red, $green, $blue)}
-
# : Converts an `rgb(red, green, blue)` triplet into a color.
-
#
-
# \{#rgba rgba($red, $green, $blue, $alpha)}
-
# : Converts an `rgba(red, green, blue, alpha)` quadruplet into a color.
-
#
-
# \{#rgba rgba($color, $alpha)}
-
# : Adds an alpha layer to any color value.
-
#
-
# \{#red red($color)}
-
# : Gets the red component of a color.
-
#
-
# \{#green green($color)}
-
# : Gets the green component of a color.
-
#
-
# \{#blue blue($color)}
-
# : Gets the blue component of a color.
-
#
-
# \{#mix mix($color-1, $color-2, \[$weight\])}
-
# : Mixes two colors together.
-
#
-
# ## HSL Functions
-
#
-
# \{#hsl hsl($hue, $saturation, $lightness)}
-
# : Converts an `hsl(hue, saturation, lightness)` triplet into a color.
-
#
-
# \{#hsla hsla($hue, $saturation, $lightness, $alpha)}
-
# : Converts an `hsla(hue, saturation, lightness, alpha)` quadruplet into a color.
-
#
-
# \{#hue hue($color)}
-
# : Gets the hue component of a color.
-
#
-
# \{#saturation saturation($color)}
-
# : Gets the saturation component of a color.
-
#
-
# \{#lightness lightness($color)}
-
# : Gets the lightness component of a color.
-
#
-
# \{#adjust_hue adjust-hue($color, $degrees)}
-
# : Changes the hue of a color.
-
#
-
# \{#lighten lighten($color, $amount)}
-
# : Makes a color lighter.
-
#
-
# \{#darken darken($color, $amount)}
-
# : Makes a color darker.
-
#
-
# \{#saturate saturate($color, $amount)}
-
# : Makes a color more saturated.
-
#
-
# \{#desaturate desaturate($color, $amount)}
-
# : Makes a color less saturated.
-
#
-
# \{#grayscale grayscale($color)}
-
# : Converts a color to grayscale.
-
#
-
# \{#complement complement($color)}
-
# : Returns the complement of a color.
-
#
-
# \{#invert invert($color)}
-
# : Returns the inverse of a color.
-
#
-
# ## Opacity Functions
-
#
-
# \{#alpha alpha($color)} / \{#opacity opacity($color)}
-
# : Gets the alpha component (opacity) of a color.
-
#
-
# \{#rgba rgba($color, $alpha)}
-
# : Add or change an alpha layer for any color value.
-
#
-
# \{#opacify opacify($color, $amount)} / \{#fade_in fade-in($color, $amount)}
-
# : Makes a color more opaque.
-
#
-
# \{#transparentize transparentize($color, $amount)} / \{#fade_out fade-out($color, $amount)}
-
# : Makes a color more transparent.
-
#
-
# ## Other Color Functions
-
#
-
# \{#adjust_color adjust-color($color, \[$red\], \[$green\], \[$blue\], \[$hue\], \[$saturation\], \[$lightness\], \[$alpha\]}
-
# : Increase or decrease any of the components of a color.
-
#
-
# \{#scale_color scale-color($color, \[$red\], \[$green\], \[$blue\], \[$saturation\], \[$lightness\], \[$alpha\]}
-
# : Fluidly scale one or more components of a color.
-
#
-
# \{#change_color change-color($color, \[$red\], \[$green\], \[$blue\], \[$hue\], \[$saturation\], \[$lightness\], \[$alpha\]}
-
# : Changes one or more properties of a color.
-
#
-
# ## String Functions
-
#
-
# \{#unquote unquote($string)}
-
# : Removes the quotes from a string.
-
#
-
# \{#quote quote($string)}
-
# : Adds quotes to a string.
-
#
-
# ## Number Functions
-
#
-
# \{#percentage percentage($value)}
-
# : Converts a unitless number to a percentage.
-
#
-
# \{#round round($value)}
-
# : Rounds a number to the nearest whole number.
-
#
-
# \{#ceil ceil($value)}
-
# : Rounds a number up to the nearest whole number.
-
#
-
# \{#floor floor($value)}
-
# : Rounds a number down to the nearest whole number.
-
#
-
# \{#abs abs($value)}
-
# : Returns the absolute value of a number.
-
#
-
# ## List Functions {#list-functions}
-
#
-
# \{#length length($list)}
-
# : Returns the length of a list.
-
#
-
# \{#nth nth($list, $n)}
-
# : Returns a specific item in a list.
-
#
-
# \{#join join($list1, $list2, \[$separator\])}
-
# : Joins together two lists into one.
-
#
-
# ## Introspection Functions
-
#
-
# \{#type_of type-of($value)}
-
# : Returns the type of a value.
-
#
-
# \{#unit unit($number)}
-
# : Returns the units associated with a number.
-
#
-
# \{#unitless unitless($number)}
-
# : Returns whether a number has units or not.
-
#
-
# \{#comparable comparable($number-1, $number-2)}
-
# : Returns whether two numbers can be added or compared.
-
#
-
# ## Miscellaneous Functions
-
#
-
# \{#if if($condition, $if-true, $if-false)}
-
# : Returns one of two values, depending on whether or not a condition is true.
-
#
-
# ## Adding Custom Functions
-
#
-
# New Sass functions can be added by adding Ruby methods to this module.
-
# For example:
-
#
-
# module Sass::Script::Functions
-
# def reverse(string)
-
# assert_type string, :String
-
# Sass::Script::String.new(string.value.reverse)
-
# end
-
# declare :reverse, :args => [:string]
-
# end
-
#
-
# Calling {declare} tells Sass the argument names for your function.
-
# If omitted, the function will still work, but will not be able to accept keyword arguments.
-
# {declare} can also allow your function to take arbitrary keyword arguments.
-
#
-
# There are a few things to keep in mind when modifying this module.
-
# First of all, the arguments passed are {Sass::Script::Literal} objects.
-
# Literal objects are also expected to be returned.
-
# This means that Ruby values must be unwrapped and wrapped.
-
#
-
# Most Literal objects support the {Sass::Script::Literal#value value} accessor
-
# for getting their Ruby values.
-
# Color objects, though, must be accessed using {Sass::Script::Color#rgb rgb},
-
# {Sass::Script::Color#red red}, {Sass::Script::Color#blue green}, or {Sass::Script::Color#blue blue}.
-
#
-
# Second, making Ruby functions accessible from Sass introduces the temptation
-
# to do things like database access within stylesheets.
-
# This is generally a bad idea;
-
# since Sass files are by default only compiled once,
-
# dynamic code is not a great fit.
-
#
-
# If you really, really need to compile Sass on each request,
-
# first make sure you have adequate caching set up.
-
# Then you can use {Sass::Engine} to render the code,
-
# using the {file:SASS_REFERENCE.md#custom-option `options` parameter}
-
# to pass in data that {EvaluationContext#options can be accessed}
-
# from your Sass functions.
-
#
-
# Within one of the functions in this module,
-
# methods of {EvaluationContext} can be used.
-
#
-
# ### Caveats
-
#
-
# When creating new {Literal} objects within functions,
-
# be aware that it's not safe to call {Literal#to_s #to_s}
-
# (or other methods that use the string representation)
-
# on those objects without first setting {Node#options= the #options attribute}.
-
1
module Functions
-
1
@signatures = {}
-
-
# A class representing a Sass function signature.
-
#
-
# @attr args [Array<Symbol>] The names of the arguments to the function.
-
# @attr var_args [Boolean] Whether the function takes a variable number of arguments.
-
# @attr var_kwargs [Boolean] Whether the function takes an arbitrary set of keyword arguments.
-
1
Signature = Struct.new(:args, :var_args, :var_kwargs)
-
-
# Declare a Sass signature for a Ruby-defined function.
-
# This includes the names of the arguments,
-
# whether the function takes a variable number of arguments,
-
# and whether the function takes an arbitrary set of keyword arguments.
-
#
-
# It's not necessary to declare a signature for a function.
-
# However, without a signature it won't support keyword arguments.
-
#
-
# A single function can have multiple signatures declared
-
# as long as each one takes a different number of arguments.
-
# It's also possible to declare multiple signatures
-
# that all take the same number of arguments,
-
# but none of them but the first will be used
-
# unless the user uses keyword arguments.
-
#
-
# @param method_name [Symbol] The name of the method
-
# whose signature is being declared.
-
# @param args [Array<Symbol>] The names of the arguments for the function signature.
-
# @option options :var_args [Boolean] (false)
-
# Whether the function accepts a variable number of (unnamed) arguments
-
# in addition to the named arguments.
-
# @option options :var_kwargs [Boolean] (false)
-
# Whether the function accepts other keyword arguments
-
# in addition to those in `:args`.
-
# If this is true, the Ruby function will be passed a hash from strings
-
# to {Sass::Script::Literal}s as the last argument.
-
# In addition, if this is true and `:var_args` is not,
-
# Sass will ensure that the last argument passed is a hash.
-
#
-
# @example
-
# declare :rgba, [:hex, :alpha]
-
# declare :rgba, [:red, :green, :blue, :alpha]
-
# declare :accepts_anything, [], :var_args => true, :var_kwargs => true
-
# declare :some_func, [:foo, :bar, :baz], :var_kwargs => true
-
1
def self.declare(method_name, args, options = {})
-
49
@signatures[method_name] ||= []
-
@signatures[method_name] << Signature.new(
-
82
args.map {|s| s.to_s},
-
options[:var_args],
-
49
options[:var_kwargs])
-
end
-
-
# Determine the correct signature for the number of arguments
-
# passed in for a given function.
-
# If no signatures match, the first signature is returned for error messaging.
-
#
-
# @param method_name [Symbol] The name of the Ruby function to be called.
-
# @param arg_arity [Number] The number of unnamed arguments the function was passed.
-
# @param kwarg_arity [Number] The number of keyword arguments the function was passed.
-
#
-
# @return [{Symbol => Object}, nil]
-
# The signature options for the matching signature,
-
# or nil if no signatures are declared for this function. See {declare}.
-
1
def self.signature(method_name, arg_arity, kwarg_arity)
-
return unless @signatures[method_name]
-
@signatures[method_name].each do |signature|
-
return signature if signature.args.size == arg_arity + kwarg_arity
-
next unless signature.args.size < arg_arity + kwarg_arity
-
-
# We have enough args.
-
# Now we need to figure out which args are varargs
-
# and if the signature allows them.
-
t_arg_arity, t_kwarg_arity = arg_arity, kwarg_arity
-
if signature.args.size > t_arg_arity
-
# we transfer some kwargs arity to args arity
-
# if it does not have enough args -- assuming the names will work out.
-
t_kwarg_arity -= (signature.args.size - t_arg_arity)
-
t_arg_arity = signature.args.size
-
end
-
-
if ( t_arg_arity == signature.args.size || t_arg_arity > signature.args.size && signature.var_args ) &&
-
(t_kwarg_arity == 0 || t_kwarg_arity > 0 && signature.var_kwargs)
-
return signature
-
end
-
end
-
@signatures[method_name].first
-
end
-
-
# The context in which methods in {Script::Functions} are evaluated.
-
# That means that all instance methods of {EvaluationContext}
-
# are available to use in functions.
-
1
class EvaluationContext
-
1
include Functions
-
-
# The options hash for the {Sass::Engine} that is processing the function call
-
#
-
# @return [{Symbol => Object}]
-
1
attr_reader :options
-
-
# @param options [{Symbol => Object}] See \{#options}
-
1
def initialize(options)
-
@options = options
-
end
-
-
# Asserts that the type of a given SassScript value
-
# is the expected type (designated by a symbol).
-
#
-
# Valid types are `:Bool`, `:Color`, `:Number`, and `:String`.
-
# Note that `:String` will match both double-quoted strings
-
# and unquoted identifiers.
-
#
-
# @example
-
# assert_type value, :String
-
# assert_type value, :Number
-
# @param value [Sass::Script::Literal] A SassScript value
-
# @param type [Symbol] The name of the type the value is expected to be
-
# @param name [String, nil] The name of the argument.
-
1
def assert_type(value, type, name = nil)
-
return if value.is_a?(Sass::Script.const_get(type))
-
err = "#{value.inspect} is not a #{type.to_s.downcase}"
-
err = "$#{name}: " + err if name
-
raise ArgumentError.new(err)
-
end
-
end
-
-
1
class << self
-
# Returns whether user function with a given name exists.
-
#
-
# @param function_name [String]
-
# @return [Boolean]
-
1
alias_method :callable?, :public_method_defined?
-
-
1
private
-
1
def include(*args)
-
1
r = super
-
# We have to re-include ourselves into EvaluationContext to work around
-
# an icky Ruby restriction.
-
1
EvaluationContext.send :include, self
-
1
r
-
end
-
end
-
-
# Creates a {Color} object from red, green, and blue values.
-
#
-
# @param red [Number]
-
# A number between 0 and 255 inclusive,
-
# or between 0% and 100% inclusive
-
# @param green [Number]
-
# A number between 0 and 255 inclusive,
-
# or between 0% and 100% inclusive
-
# @param blue [Number]
-
# A number between 0 and 255 inclusive,
-
# or between 0% and 100% inclusive
-
# @see #rgba
-
# @return [Color]
-
1
def rgb(red, green, blue)
-
assert_type red, :Number
-
assert_type green, :Number
-
assert_type blue, :Number
-
-
Color.new([red, green, blue].map do |c|
-
v = c.value
-
if c.numerator_units == ["%"] && c.denominator_units.empty?
-
next v * 255 / 100.0 if (0..100).include?(v)
-
raise ArgumentError.new("Color value #{c} must be between 0% and 100% inclusive")
-
else
-
next v if (0..255).include?(v)
-
raise ArgumentError.new("Color value #{v} must be between 0 and 255 inclusive")
-
end
-
end)
-
end
-
1
declare :rgb, [:red, :green, :blue]
-
-
# @see #rgb
-
# @overload rgba(red, green, blue, alpha)
-
# Creates a {Color} object from red, green, and blue values,
-
# as well as an alpha channel indicating opacity.
-
#
-
# @param red [Number]
-
# A number between 0 and 255 inclusive
-
# @param green [Number]
-
# A number between 0 and 255 inclusive
-
# @param blue [Number]
-
# A number between 0 and 255 inclusive
-
# @param alpha [Number]
-
# A number between 0 and 1
-
# @return [Color]
-
#
-
# @overload rgba(color, alpha)
-
# Sets the opacity of a color.
-
#
-
# @example
-
# rgba(#102030, 0.5) => rgba(16, 32, 48, 0.5)
-
# rgba(blue, 0.2) => rgba(0, 0, 255, 0.2)
-
#
-
# @param color [Color]
-
# @param alpha [Number]
-
# A number between 0 and 1
-
# @return [Color]
-
1
def rgba(*args)
-
case args.size
-
when 2
-
color, alpha = args
-
-
assert_type color, :Color
-
assert_type alpha, :Number
-
-
unless (0..1).include?(alpha.value)
-
raise ArgumentError.new("Alpha channel #{alpha.value} must be between 0 and 1 inclusive")
-
end
-
-
color.with(:alpha => alpha.value)
-
when 4
-
red, green, blue, alpha = args
-
rgba(rgb(red, green, blue), alpha)
-
else
-
raise ArgumentError.new("wrong number of arguments (#{args.size} for 4)")
-
end
-
end
-
1
declare :rgba, [:red, :green, :blue, :alpha]
-
1
declare :rgba, [:color, :alpha]
-
-
# Creates a {Color} object from hue, saturation, and lightness.
-
# Uses the algorithm from the [CSS3 spec](http://www.w3.org/TR/css3-color/#hsl-color).
-
#
-
# @param hue [Number] The hue of the color.
-
# Should be between 0 and 360 degrees, inclusive
-
# @param saturation [Number] The saturation of the color.
-
# Must be between `0%` and `100%`, inclusive
-
# @param lightness [Number] The lightness of the color.
-
# Must be between `0%` and `100%`, inclusive
-
# @return [Color] The resulting color
-
# @see #hsla
-
# @raise [ArgumentError] if `saturation` or `lightness` are out of bounds
-
1
def hsl(hue, saturation, lightness)
-
hsla(hue, saturation, lightness, Number.new(1))
-
end
-
1
declare :hsl, [:hue, :saturation, :lightness]
-
-
# Creates a {Color} object from hue, saturation, and lightness,
-
# as well as an alpha channel indicating opacity.
-
# Uses the algorithm from the [CSS3 spec](http://www.w3.org/TR/css3-color/#hsl-color).
-
#
-
# @param hue [Number] The hue of the color.
-
# Should be between 0 and 360 degrees, inclusive
-
# @param saturation [Number] The saturation of the color.
-
# Must be between `0%` and `100%`, inclusive
-
# @param lightness [Number] The lightness of the color.
-
# Must be between `0%` and `100%`, inclusive
-
# @param alpha [Number] The opacity of the color.
-
# Must be between 0 and 1, inclusive
-
# @return [Color] The resulting color
-
# @see #hsl
-
# @raise [ArgumentError] if `saturation`, `lightness`, or `alpha` are out of bounds
-
1
def hsla(hue, saturation, lightness, alpha)
-
assert_type hue, :Number
-
assert_type saturation, :Number
-
assert_type lightness, :Number
-
assert_type alpha, :Number
-
-
unless (0..1).include?(alpha.value)
-
raise ArgumentError.new("Alpha channel #{alpha.value} must be between 0 and 1")
-
end
-
-
original_s = saturation
-
original_l = lightness
-
# This algorithm is from http://www.w3.org/TR/css3-color#hsl-color
-
h, s, l = [hue, saturation, lightness].map { |a| a.value }
-
raise ArgumentError.new("Saturation #{s} must be between 0% and 100%") unless (0..100).include?(s)
-
raise ArgumentError.new("Lightness #{l} must be between 0% and 100%") unless (0..100).include?(l)
-
-
Color.new(:hue => h, :saturation => s, :lightness => l, :alpha => alpha.value)
-
end
-
1
declare :hsla, [:hue, :saturation, :lightness, :alpha]
-
-
# Returns the red component of a color.
-
#
-
# @param color [Color]
-
# @return [Number]
-
# @raise [ArgumentError] If `color` isn't a color
-
1
def red(color)
-
assert_type color, :Color
-
Sass::Script::Number.new(color.red)
-
end
-
1
declare :red, [:color]
-
-
# Returns the green component of a color.
-
#
-
# @param color [Color]
-
# @return [Number]
-
# @raise [ArgumentError] If `color` isn't a color
-
1
def green(color)
-
assert_type color, :Color
-
Sass::Script::Number.new(color.green)
-
end
-
1
declare :green, [:color]
-
-
# Returns the blue component of a color.
-
#
-
# @param color [Color]
-
# @return [Number]
-
# @raise [ArgumentError] If `color` isn't a color
-
1
def blue(color)
-
assert_type color, :Color
-
Sass::Script::Number.new(color.blue)
-
end
-
1
declare :blue, [:color]
-
-
# Returns the hue component of a color.
-
#
-
# See [the CSS3 HSL specification](http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV).
-
#
-
# Calculated from RGB where necessary via [this algorithm](http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV).
-
#
-
# @param color [Color]
-
# @return [Number] between 0deg and 360deg
-
# @see #adjust_hue
-
# @raise [ArgumentError] if `color` isn't a color
-
1
def hue(color)
-
assert_type color, :Color
-
Sass::Script::Number.new(color.hue, ["deg"])
-
end
-
1
declare :hue, [:color]
-
-
# Returns the saturation component of a color.
-
#
-
# See [the CSS3 HSL specification](http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV).
-
#
-
# Calculated from RGB where necessary via [this algorithm](http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV).
-
#
-
# @param color [Color]
-
# @return [Number] between 0% and 100%
-
# @see #saturate
-
# @see #desaturate
-
# @raise [ArgumentError] if `color` isn't a color
-
1
def saturation(color)
-
assert_type color, :Color
-
Sass::Script::Number.new(color.saturation, ["%"])
-
end
-
1
declare :saturation, [:color]
-
-
# Returns the hue component of a color.
-
#
-
# See [the CSS3 HSL specification](http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV).
-
#
-
# Calculated from RGB where necessary via [this algorithm](http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV).
-
#
-
# @param color [Color]
-
# @return [Number] between 0% and 100%
-
# @see #lighten
-
# @see #darken
-
# @raise [ArgumentError] if `color` isn't a color
-
1
def lightness(color)
-
assert_type color, :Color
-
Sass::Script::Number.new(color.lightness, ["%"])
-
end
-
1
declare :lightness, [:color]
-
-
# Returns the alpha component (opacity) of a color.
-
# This is 1 unless otherwise specified.
-
#
-
# This function also supports the proprietary Microsoft
-
# `alpha(opacity=20)` syntax.
-
#
-
# @overload def alpha(color)
-
# @param color [Color]
-
# @return [Number]
-
# @see #opacify
-
# @see #transparentize
-
# @raise [ArgumentError] If `color` isn't a color
-
1
def alpha(*args)
-
if args.all? do |a|
-
a.is_a?(Sass::Script::String) && a.type == :identifier &&
-
a.value =~ /^[a-zA-Z]+\s*=/
-
end
-
# Support the proprietary MS alpha() function
-
return Sass::Script::String.new("alpha(#{args.map {|a| a.to_s}.join(", ")})")
-
end
-
-
opacity(*args)
-
end
-
1
declare :alpha, [:color]
-
-
# Returns the alpha component (opacity) of a color.
-
# This is 1 unless otherwise specified.
-
#
-
# @param color [Color]
-
# @return [Number]
-
# @see #opacify
-
# @see #transparentize
-
# @raise [ArgumentError] If `color` isn't a color
-
1
def opacity(color)
-
assert_type color, :Color
-
Sass::Script::Number.new(color.alpha)
-
end
-
1
declare :opacity, [:color]
-
-
# Makes a color more opaque.
-
# Takes a color and an amount between 0 and 1,
-
# and returns a color with the opacity increased by that value.
-
#
-
# @example
-
# opacify(rgba(0, 0, 0, 0.5), 0.1) => rgba(0, 0, 0, 0.6)
-
# opacify(rgba(0, 0, 17, 0.8), 0.2) => #001
-
# @param color [Color]
-
# @param amount [Number]
-
# @return [Color]
-
# @see #transparentize
-
# @raise [ArgumentError] If `color` isn't a color,
-
# or `number` isn't a number between 0 and 1
-
1
def opacify(color, amount)
-
_adjust(color, amount, :alpha, 0..1, :+)
-
end
-
1
declare :opacify, [:color, :amount]
-
-
1
alias_method :fade_in, :opacify
-
1
declare :fade_in, [:color, :amount]
-
-
# Makes a color more transparent.
-
# Takes a color and an amount between 0 and 1,
-
# and returns a color with the opacity decreased by that value.
-
#
-
# @example
-
# transparentize(rgba(0, 0, 0, 0.5), 0.1) => rgba(0, 0, 0, 0.4)
-
# transparentize(rgba(0, 0, 0, 0.8), 0.2) => rgba(0, 0, 0, 0.6)
-
# @param color [Color]
-
# @param amount [Number]
-
# @return [Color]
-
# @see #opacify
-
# @raise [ArgumentError] If `color` isn't a color,
-
# or `number` isn't a number between 0 and 1
-
1
def transparentize(color, amount)
-
_adjust(color, amount, :alpha, 0..1, :-)
-
end
-
1
declare :transparentize, [:color, :amount]
-
-
1
alias_method :fade_out, :transparentize
-
1
declare :fade_out, [:color, :amount]
-
-
# Makes a color lighter.
-
# Takes a color and an amount between 0% and 100%,
-
# and returns a color with the lightness increased by that value.
-
#
-
# @example
-
# lighten(hsl(0, 0%, 0%), 30%) => hsl(0, 0, 30)
-
# lighten(#800, 20%) => #e00
-
# @param color [Color]
-
# @param amount [Number]
-
# @return [Color]
-
# @see #darken
-
# @raise [ArgumentError] If `color` isn't a color,
-
# or `number` isn't a number between 0% and 100%
-
1
def lighten(color, amount)
-
_adjust(color, amount, :lightness, 0..100, :+, "%")
-
end
-
1
declare :lighten, [:color, :amount]
-
-
# Makes a color darker.
-
# Takes a color and an amount between 0% and 100%,
-
# and returns a color with the lightness decreased by that value.
-
#
-
# @example
-
# darken(hsl(25, 100%, 80%), 30%) => hsl(25, 100%, 50%)
-
# darken(#800, 20%) => #200
-
# @param color [Color]
-
# @param amount [Number]
-
# @return [Color]
-
# @see #lighten
-
# @raise [ArgumentError] If `color` isn't a color,
-
# or `number` isn't a number between 0% and 100%
-
1
def darken(color, amount)
-
_adjust(color, amount, :lightness, 0..100, :-, "%")
-
end
-
1
declare :darken, [:color, :amount]
-
-
# Makes a color more saturated.
-
# Takes a color and an amount between 0% and 100%,
-
# and returns a color with the saturation increased by that value.
-
#
-
# @example
-
# saturate(hsl(120, 30%, 90%), 20%) => hsl(120, 50%, 90%)
-
# saturate(#855, 20%) => #9e3f3f
-
# @param color [Color]
-
# @param amount [Number]
-
# @return [Color]
-
# @see #desaturate
-
# @raise [ArgumentError] If `color` isn't a color,
-
# or `number` isn't a number between 0% and 100%
-
1
def saturate(color, amount)
-
_adjust(color, amount, :saturation, 0..100, :+, "%")
-
end
-
1
declare :saturate, [:color, :amount]
-
-
# Makes a color less saturated.
-
# Takes a color and an amount between 0% and 100%,
-
# and returns a color with the saturation decreased by that value.
-
#
-
# @example
-
# desaturate(hsl(120, 30%, 90%), 20%) => hsl(120, 10%, 90%)
-
# desaturate(#855, 20%) => #726b6b
-
# @param color [Color]
-
# @param amount [Number]
-
# @return [Color]
-
# @see #saturate
-
# @raise [ArgumentError] If `color` isn't a color,
-
# or `number` isn't a number between 0% and 100%
-
1
def desaturate(color, amount)
-
_adjust(color, amount, :saturation, 0..100, :-, "%")
-
end
-
1
declare :desaturate, [:color, :amount]
-
-
# Changes the hue of a color while retaining the lightness and saturation.
-
# Takes a color and a number of degrees (usually between -360deg and 360deg),
-
# and returns a color with the hue rotated by that value.
-
#
-
# @example
-
# adjust-hue(hsl(120, 30%, 90%), 60deg) => hsl(180, 30%, 90%)
-
# adjust-hue(hsl(120, 30%, 90%), 060deg) => hsl(60, 30%, 90%)
-
# adjust-hue(#811, 45deg) => #886a11
-
# @param color [Color]
-
# @param amount [Number]
-
# @return [Color]
-
# @raise [ArgumentError] If `color` isn't a color, or `number` isn't a number
-
1
def adjust_hue(color, degrees)
-
assert_type color, :Color
-
assert_type degrees, :Number
-
color.with(:hue => color.hue + degrees.value)
-
end
-
1
declare :adjust_hue, [:color, :degrees]
-
-
# Adjusts one or more properties of a color.
-
# This can change the red, green, blue, hue, saturation, value, and alpha properties.
-
# The properties are specified as keyword arguments,
-
# and are added to or subtracted from the color's current value for that property.
-
#
-
# `$red`, `$green`, and `$blue` properties should be between 0 and 255.
-
# `$saturation` and `$lightness` should be between 0% and 100%.
-
# `$alpha` should be between 0 and 1.
-
#
-
# All properties are optional.
-
# You can't specify both RGB properties (`$red`, `$green`, `$blue`)
-
# and HSL properties (`$hue`, `$saturation`, `$value`) at the same time.
-
#
-
# @example
-
# adjust-color(#102030, $blue: 5) => #102035
-
# adjust-color(#102030, $red: -5, $blue: 5) => #0b2035
-
# adjust-color(hsl(25, 100%, 80%), $lightness: -30%, $alpha: -0.4) => hsla(25, 100%, 50%, 0.6)
-
# @param color [Color]
-
# @param red [Number]
-
# @param green [Number]
-
# @param blue [Number]
-
# @param hue [Number]
-
# @param saturation [Number]
-
# @param lightness [Number]
-
# @param alpha [Number]
-
# @return [Color]
-
# @raise [ArgumentError] if `color` is not a color,
-
# if any keyword argument is not a number,
-
# if any keyword argument is not in the legal range,
-
# if an unexpected keyword argument is given,
-
# or if both HSL and RGB properties are given.
-
1
def adjust_color(color, kwargs)
-
assert_type color, :Color
-
with = Sass::Util.map_hash({
-
"red" => [-255..255, ""],
-
"green" => [-255..255, ""],
-
"blue" => [-255..255, ""],
-
"hue" => nil,
-
"saturation" => [-100..100, "%"],
-
"lightness" => [-100..100, "%"],
-
"alpha" => [-1..1, ""]
-
}) do |name, (range, units)|
-
-
next unless val = kwargs.delete(name)
-
assert_type val, :Number, name
-
if range && !range.include?(val.value)
-
raise ArgumentError.new("$#{name}: Amount #{val} must be between #{range.first}#{units} and #{range.last}#{units}")
-
end
-
adjusted = color.send(name) + val.value
-
adjusted = [0, Sass::Util.restrict(adjusted, range)].max if range
-
[name.to_sym, adjusted]
-
end
-
-
unless kwargs.empty?
-
name, val = kwargs.to_a.first
-
raise ArgumentError.new("Unknown argument $#{name} (#{val})")
-
end
-
-
color.with(with)
-
end
-
1
declare :adjust_color, [:color], :var_kwargs => true
-
-
# Scales one or more properties of a color by a percentage value.
-
# Unlike \{#adjust_color adjust-color}, which changes a color's properties by fixed amounts,
-
# \{#scale_color scale-color} fluidly changes them based on how high or low they already are.
-
# That means that lightening an already-light color with \{#scale_color scale-color}
-
# won't change the lightness much,
-
# but lightening a dark color by the same amount will change it more dramatically.
-
# This has the benefit of making `scale-color($color, ...)` have a similar effect
-
# regardless of what `$color` is.
-
#
-
# For example, the lightness of a color can be anywhere between 0 and 100.
-
# If `scale-color($color, $lightness: 40%)` is called, the resulting color's lightness
-
# will be 40% of the way between its original lightness and 100.
-
# If `scale-color($color, $lightness: -40%)` is called instead,
-
# the lightness will be 40% of the way between the original and 0.
-
#
-
# This can change the red, green, blue, saturation, value, and alpha properties.
-
# The properties are specified as keyword arguments.
-
# All arguments should be percentages between 0% and 100%.
-
#
-
# All properties are optional.
-
# You can't specify both RGB properties (`$red`, `$green`, `$blue`)
-
# and HSL properties (`$saturation`, `$value`) at the same time.
-
#
-
# @example
-
# scale-color(hsl(120, 70, 80), $lightness: 50%) => hsl(120, 70, 90)
-
# scale-color(rgb(200, 150, 170), $green: -40%, $blue: 70%) => rgb(200, 90, 229)
-
# scale-color(hsl(200, 70, 80), $saturation: -90%, $alpha: -30%) => hsla(200, 7, 80, 0.7)
-
# @param color [Color]
-
# @param red [Number]
-
# @param green [Number]
-
# @param blue [Number]
-
# @param saturation [Number]
-
# @param lightness [Number]
-
# @param alpha [Number]
-
# @return [Color]
-
# @raise [ArgumentError] if `color` is not a color,
-
# if any keyword argument is not a percentage between 0% and 100%,
-
# if an unexpected keyword argument is given,
-
# or if both HSL and RGB properties are given.
-
1
def scale_color(color, kwargs)
-
assert_type color, :Color
-
with = Sass::Util.map_hash({
-
"red" => 255,
-
"green" => 255,
-
"blue" => 255,
-
"saturation" => 100,
-
"lightness" => 100,
-
"alpha" => 1
-
}) do |name, max|
-
-
next unless val = kwargs.delete(name)
-
assert_type val, :Number, name
-
if !(val.numerator_units == ['%'] && val.denominator_units.empty?)
-
raise ArgumentError.new("$#{name}: Amount #{val} must be a % (e.g. #{val.value}%)")
-
elsif !(-100..100).include?(val.value)
-
raise ArgumentError.new("$#{name}: Amount #{val} must be between -100% and 100%")
-
end
-
-
current = color.send(name)
-
scale = val.value/100.0
-
diff = scale > 0 ? max - current : current
-
[name.to_sym, current + diff*scale]
-
end
-
-
unless kwargs.empty?
-
name, val = kwargs.to_a.first
-
raise ArgumentError.new("Unknown argument $#{name} (#{val})")
-
end
-
-
color.with(with)
-
end
-
1
declare :scale_color, [:color], :var_kwargs => true
-
-
# Changes one or more properties of a color.
-
# This can change the red, green, blue, hue, saturation, value, and alpha properties.
-
# The properties are specified as keyword arguments,
-
# and replace the color's current value for that property.
-
#
-
# `$red`, `$green`, and `$blue` properties should be between 0 and 255.
-
# `$saturation` and `$lightness` should be between 0% and 100%.
-
# `$alpha` should be between 0 and 1.
-
#
-
# All properties are optional.
-
# You can't specify both RGB properties (`$red`, `$green`, `$blue`)
-
# and HSL properties (`$hue`, `$saturation`, `$value`) at the same time.
-
#
-
# @example
-
# change-color(#102030, $blue: 5) => #102005
-
# change-color(#102030, $red: 120, $blue: 5) => #782005
-
# change-color(hsl(25, 100%, 80%), $lightness: 40%, $alpha: 0.8) => hsla(25, 100%, 40%, 0.8)
-
# @param color [Color]
-
# @param red [Number]
-
# @param green [Number]
-
# @param blue [Number]
-
# @param hue [Number]
-
# @param saturation [Number]
-
# @param lightness [Number]
-
# @param alpha [Number]
-
# @return [Color]
-
# @raise [ArgumentError] if `color` is not a color,
-
# if any keyword argument is not a number,
-
# if any keyword argument is not in the legal range,
-
# if an unexpected keyword argument is given,
-
# or if both HSL and RGB properties are given.
-
1
def change_color(color, kwargs)
-
assert_type color, :Color
-
with = Sass::Util.map_hash(%w[red green blue hue saturation lightness alpha]) do |name, max|
-
next unless val = kwargs.delete(name)
-
assert_type val, :Number, name
-
[name.to_sym, val.value]
-
end
-
-
unless kwargs.empty?
-
name, val = kwargs.to_a.first
-
raise ArgumentError.new("Unknown argument $#{name} (#{val})")
-
end
-
-
color.with(with)
-
end
-
1
declare :change_color, [:color], :var_kwargs => true
-
-
# Mixes together two colors.
-
# Specifically, takes the average of each of the RGB components,
-
# optionally weighted by the given percentage.
-
# The opacity of the colors is also considered when weighting the components.
-
#
-
# The weight specifies the amount of the first color that should be included
-
# in the returned color.
-
# The default, 50%, means that half the first color
-
# and half the second color should be used.
-
# 25% means that a quarter of the first color
-
# and three quarters of the second color should be used.
-
#
-
# @example
-
# mix(#f00, #00f) => #7f007f
-
# mix(#f00, #00f, 25%) => #3f00bf
-
# mix(rgba(255, 0, 0, 0.5), #00f) => rgba(63, 0, 191, 0.75)
-
# @overload mix(color1, color2, weight: 50%)
-
# @param color1 [Color]
-
# @param color2 [Color]
-
# @param weight [Number] between 0% and 100%
-
# @return [Color]
-
# @raise [ArgumentError] if `color1` or `color2` aren't colors,
-
# or `weight` isn't a number between 0% and 100%
-
1
def mix(color1, color2, weight = Number.new(50))
-
assert_type color1, :Color
-
assert_type color2, :Color
-
assert_type weight, :Number
-
-
unless (0..100).include?(weight.value)
-
raise ArgumentError.new("Weight #{weight} must be between 0% and 100%")
-
end
-
-
# This algorithm factors in both the user-provided weight
-
# and the difference between the alpha values of the two colors
-
# to decide how to perform the weighted average of the two RGB values.
-
#
-
# It works by first normalizing both parameters to be within [-1, 1],
-
# where 1 indicates "only use color1", -1 indicates "only use color 0",
-
# and all values in between indicated a proportionately weighted average.
-
#
-
# Once we have the normalized variables w and a,
-
# we apply the formula (w + a)/(1 + w*a)
-
# to get the combined weight (in [-1, 1]) of color1.
-
# This formula has two especially nice properties:
-
#
-
# * When either w or a are -1 or 1, the combined weight is also that number
-
# (cases where w * a == -1 are undefined, and handled as a special case).
-
#
-
# * When a is 0, the combined weight is w, and vice versa
-
#
-
# Finally, the weight of color1 is renormalized to be within [0, 1]
-
# and the weight of color2 is given by 1 minus the weight of color1.
-
p = (weight.value/100.0).to_f
-
w = p*2 - 1
-
a = color1.alpha - color2.alpha
-
-
w1 = (((w * a == -1) ? w : (w + a)/(1 + w*a)) + 1)/2.0
-
w2 = 1 - w1
-
-
rgb = color1.rgb.zip(color2.rgb).map {|v1, v2| v1*w1 + v2*w2}
-
alpha = color1.alpha*p + color2.alpha*(1-p)
-
Color.new(rgb + [alpha])
-
end
-
1
declare :mix, [:color_1, :color_2]
-
1
declare :mix, [:color_1, :color_2, :weight]
-
-
# @overload grayscale(color)
-
# Converts a color to grayscale.
-
# This is identical to `desaturate(color, 100%)`.
-
#
-
# @param color [Color]
-
# @return [Color]
-
# @raise [ArgumentError] if `color` isn't a color
-
# @see #desaturate
-
# @overload grayscale(number)
-
# Returns an unquoted string `grayscale(number)`, as though the function
-
# were not defined. This is for the `grayscale` function used in
-
# `-webkit-filter`.
-
#
-
# @param number [Number]
-
# @return [Sass::Script::String]
-
1
def grayscale(color)
-
return Sass::Script::String.new("grayscale(#{color})") if color.is_a?(Sass::Script::Number)
-
desaturate color, Number.new(100)
-
end
-
1
declare :grayscale, [:color]
-
-
# Returns the complement of a color.
-
# This is identical to `adjust-hue(color, 180deg)`.
-
#
-
# @param color [Color]
-
# @return [Color]
-
# @raise [ArgumentError] if `color` isn't a color
-
# @see #adjust_hue #adjust-hue
-
1
def complement(color)
-
adjust_hue color, Number.new(180)
-
end
-
1
declare :complement, [:color]
-
-
# Returns the inverse (negative) of a color.
-
# The red, green, and blue values are inverted, while the opacity is left alone.
-
#
-
# @param color [Color]
-
# @return [Color]
-
# @raise [ArgumentError] if `color` isn't a color
-
1
def invert(color)
-
assert_type color, :Color
-
color.with(
-
:red => (255 - color.red),
-
:green => (255 - color.green),
-
:blue => (255 - color.blue))
-
end
-
-
# Removes quotes from a string if the string is quoted,
-
# or returns the same string if it's not.
-
#
-
# @param string [String]
-
# @return [String]
-
# @raise [ArgumentError] if `string` isn't a string
-
# @see #quote
-
# @example
-
# unquote("foo") => foo
-
# unquote(foo) => foo
-
1
def unquote(string)
-
if string.is_a?(Sass::Script::String)
-
Sass::Script::String.new(string.value, :identifier)
-
else
-
string
-
end
-
end
-
1
declare :unquote, [:string]
-
-
# Add quotes to a string if the string isn't quoted,
-
# or returns the same string if it is.
-
#
-
# @param string [String]
-
# @return [String]
-
# @raise [ArgumentError] if `string` isn't a string
-
# @see #unquote
-
# @example
-
# quote("foo") => "foo"
-
# quote(foo) => "foo"
-
1
def quote(string)
-
assert_type string, :String
-
Sass::Script::String.new(string.value, :string)
-
end
-
1
declare :quote, [:string]
-
-
# Inspects the type of the argument, returning it as an unquoted string.
-
#
-
# @example
-
# type-of(100px) => number
-
# type-of(asdf) => string
-
# type-of("asdf") => string
-
# type-of(true) => bool
-
# type-of(#fff) => color
-
# type-of(blue) => color
-
# @param value [Literal] The object to inspect
-
# @return [String] The unquoted string name of the literal's type
-
1
def type_of(value)
-
Sass::Script::String.new(value.class.name.gsub(/Sass::Script::/,'').downcase)
-
end
-
1
declare :type_of, [:value]
-
-
# Inspects the unit of the number, returning it as a quoted string.
-
# Complex units are sorted in alphabetical order by numerator and denominator.
-
#
-
# @example
-
# unit(100) => ""
-
# unit(100px) => "px"
-
# unit(3em) => "em"
-
# unit(10px * 5em) => "em*px"
-
# unit(10px * 5em / 30cm / 1rem) => "em*px/cm*rem"
-
# @param number [Literal] The number to inspect
-
# @return [String] The unit(s) of the number
-
# @raise [ArgumentError] if `number` isn't a number
-
1
def unit(number)
-
assert_type number, :Number
-
Sass::Script::String.new(number.unit_str, :string)
-
end
-
1
declare :unit, [:number]
-
-
# Inspects the unit of the number, returning a boolean indicating if it is unitless.
-
#
-
# @example
-
# unitless(100) => true
-
# unitless(100px) => false
-
# @param number [Literal] The number to inspect
-
# @return [Bool] Whether or not the number is unitless
-
# @raise [ArgumentError] if `number` isn't a number
-
1
def unitless(number)
-
assert_type number, :Number
-
Sass::Script::Bool.new(number.unitless?)
-
end
-
1
declare :unitless, [:number]
-
-
# Returns true if two numbers are similar enough to be added, subtracted, or compared.
-
#
-
# @example
-
# comparable(2px, 1px) => true
-
# comparable(100px, 3em) => false
-
# comparable(10cm, 3mm) => true
-
# @param number_1 [Number]
-
# @param number_2 [Number]
-
# @return [Bool] indicating if the numbers can be compared.
-
# @raise [ArgumentError] if `number_1` or `number_2` aren't numbers
-
1
def comparable(number_1, number_2)
-
assert_type number_1, :Number
-
assert_type number_2, :Number
-
Sass::Script::Bool.new(number_1.comparable_to?(number_2))
-
end
-
1
declare :comparable, [:number_1, :number_2]
-
-
# Converts a decimal number to a percentage.
-
#
-
# @example
-
# percentage(100px / 50px) => 200%
-
# @param value [Number] The decimal number to convert to a percentage
-
# @return [Number] The percentage
-
# @raise [ArgumentError] If `value` isn't a unitless number
-
1
def percentage(value)
-
unless value.is_a?(Sass::Script::Number) && value.unitless?
-
raise ArgumentError.new("#{value.inspect} is not a unitless number")
-
end
-
Sass::Script::Number.new(value.value * 100, ['%'])
-
end
-
1
declare :percentage, [:value]
-
-
# Rounds a number to the nearest whole number.
-
#
-
# @example
-
# round(10.4px) => 10px
-
# round(10.6px) => 11px
-
# @param value [Number] The number
-
# @return [Number] The rounded number
-
# @raise [ArgumentError] if `value` isn't a number
-
1
def round(value)
-
numeric_transformation(value) {|n| n.round}
-
end
-
1
declare :round, [:value]
-
-
# Rounds a number up to the nearest whole number.
-
#
-
# @example
-
# ciel(10.4px) => 11px
-
# ciel(10.6px) => 11px
-
# @param value [Number] The number
-
# @return [Number] The rounded number
-
# @raise [ArgumentError] if `value` isn't a number
-
1
def ceil(value)
-
numeric_transformation(value) {|n| n.ceil}
-
end
-
1
declare :ceil, [:value]
-
-
# Rounds down to the nearest whole number.
-
#
-
# @example
-
# floor(10.4px) => 10px
-
# floor(10.6px) => 10px
-
# @param value [Number] The number
-
# @return [Number] The rounded number
-
# @raise [ArgumentError] if `value` isn't a number
-
1
def floor(value)
-
numeric_transformation(value) {|n| n.floor}
-
end
-
1
declare :floor, [:value]
-
-
# Finds the absolute value of a number.
-
#
-
# @example
-
# abs(10px) => 10px
-
# abs(-10px) => 10px
-
# @param value [Number] The number
-
# @return [Number] The absolute value
-
# @raise [ArgumentError] if `value` isn't a number
-
1
def abs(value)
-
numeric_transformation(value) {|n| n.abs}
-
end
-
1
declare :abs, [:value]
-
-
# Return the length of a list.
-
#
-
# @example
-
# length(10px) => 1
-
# length(10px 20px 30px) => 3
-
# @param list [Literal] The list
-
# @return [Number] The length
-
1
def length(list)
-
Sass::Script::Number.new(list.to_a.size)
-
end
-
1
declare :length, [:list]
-
-
# Gets the nth item in a list.
-
#
-
# Note that unlike some languages, the first item in a Sass list is number 1,
-
# the second number 2, and so forth.
-
#
-
# @example
-
# nth(10px 20px 30px, 1) => 10px
-
# nth((Helvetica, Arial, sans-serif), 3) => sans-serif
-
# @param list [Literal] The list
-
# @param n [Number] The index into the list
-
# @return [Literal] The nth item in the list
-
# @raise [ArgumentError] If `n` isn't an integer between 1 and the list's length.
-
1
def nth(list, n)
-
assert_type n, :Number
-
if !n.int?
-
raise ArgumentError.new("List index #{n} must be an integer")
-
elsif n.to_i < 1
-
raise ArgumentError.new("List index #{n} must be greater than or equal to 1")
-
elsif list.to_a.size == 0
-
raise ArgumentError.new("List index is #{n} but list has no items")
-
elsif n.to_i > (size = list.to_a.size)
-
raise ArgumentError.new("List index is #{n} but list is only #{size} item#{'s' if size != 1} long")
-
end
-
-
list.to_a[n.to_i - 1]
-
end
-
1
declare :nth, [:list, :n]
-
-
# Joins together two lists into a new list.
-
#
-
# Unless the `$separator` argument is passed,
-
# if one list is comma-separated and one is space-separated,
-
# the first parameter's separator is used for the resulting list.
-
# If the lists have only one item each, spaces are used for the resulting list.
-
#
-
# @example
-
# join(10px 20px, 30px 40px) => 10px 20px 30px 40px
-
# join((blue, red), (#abc, #def)) => blue, red, #abc, #def
-
# join(10px, 20px) => 10px 20px
-
# join(10px, 20px, comma) => 10px, 20px
-
# join((blue, red), (#abc, #def), space) => blue red #abc #def
-
# @overload join(list1, list2, separator: auto)
-
# @param list1 [Literal] The first list to join
-
# @param list2 [Literal] The second list to join
-
# @param separator [String] How the list separator (comma or space) should be determined.
-
# If this is `comma` or `space`, that is always the separator;
-
# if this is `auto` (the default), the separator is determined as explained above.
-
1
def join(list1, list2, separator = Sass::Script::String.new("auto"))
-
assert_type separator, :String
-
unless %w[auto space comma].include?(separator.value)
-
raise ArgumentError.new("Separator name must be space, comma, or auto")
-
end
-
sep1 = list1.separator if list1.is_a?(Sass::Script::List) && !list1.value.empty?
-
sep2 = list2.separator if list2.is_a?(Sass::Script::List) && !list2.value.empty?
-
Sass::Script::List.new(
-
list1.to_a + list2.to_a,
-
if separator.value == 'auto'
-
sep1 || sep2 || :space
-
else
-
separator.value.to_sym
-
end)
-
end
-
1
declare :join, [:list1, :list2]
-
1
declare :join, [:list1, :list2, :separator]
-
-
# Appends a single value onto the end of a list.
-
#
-
# Unless the `$separator` argument is passed,
-
# if the list has only one item,
-
# the resulting list will be space-separated.
-
#
-
# @example
-
# append(10px 20px, 30px) => 10px 20px 30px
-
# append((blue, red), green) => blue, red, green
-
# append(10px 20px, 30px 40px) => 10px 20px (30px 40px)
-
# join(10px, 20px, comma) => 10px, 20px
-
# join((blue, red), green, space) => blue red green
-
# @overload join(list, val, separator: auto)
-
# @param list1 [Literal] The first list to join
-
# @param list2 [Literal] The second list to join
-
# @param separator [String] How the list separator (comma or space) should be determined.
-
# If this is `comma` or `space`, that is always the separator;
-
# if this is `auto` (the default), the separator is determined as explained above.
-
1
def append(list, val, separator = Sass::Script::String.new("auto"))
-
assert_type separator, :String
-
unless %w[auto space comma].include?(separator.value)
-
raise ArgumentError.new("Separator name must be space, comma, or auto")
-
end
-
sep = list.separator if list.is_a?(Sass::Script::List)
-
Sass::Script::List.new(
-
list.to_a + [val],
-
if separator.value == 'auto'
-
sep || :space
-
else
-
separator.value.to_sym
-
end)
-
end
-
1
declare :append, [:list, :val]
-
1
declare :append, [:list, :val, :separator]
-
-
# Combines several lists into a single comma separated list
-
# space separated lists.
-
#
-
# The length of the resulting list is the length of the
-
# shortest list.
-
#
-
# @example
-
# zip(1px 1px 3px, solid dashed solid, red green blue)
-
# => 1px solid red, 1px dashed green, 3px solid blue
-
1
def zip(*lists)
-
length = nil
-
values = []
-
lists.each do |list|
-
assert_type list, :List
-
values << list.value.dup
-
length = length.nil? ? list.value.length : [length, list.value.length].min
-
end
-
values.each do |value|
-
value.slice!(length)
-
end
-
new_list_value = values.first.zip(*values[1..-1])
-
List.new(new_list_value.map{|list| List.new(list, :space)}, :comma)
-
end
-
1
declare :zip, [], :var_args => true
-
-
-
# Returns the position of the given value within the given
-
# list. If not found, returns false.
-
#
-
# @example
-
# index(1px solid red, solid) => 2
-
# index(1px solid red, dashed) => false
-
1
def index(list, value)
-
assert_type list, :List
-
index = list.value.index {|e| e.eq(value).to_bool }
-
if index
-
Number.new(index + 1)
-
else
-
Bool.new(false)
-
end
-
end
-
1
declare :index, [:list, :value]
-
-
# Returns one of two values based on the truth value of the first argument.
-
#
-
# @example
-
# if(true, 1px, 2px) => 1px
-
# if(false, 1px, 2px) => 2px
-
# @param condition [Bool] Whether the first or second value will be returned.
-
# @param if_true [Literal] The value that will be returned if `$condition` is true.
-
# @param if_false [Literal] The value that will be returned if `$condition` is false.
-
1
def if(condition, if_true, if_false)
-
if condition.to_bool
-
if_true
-
else
-
if_false
-
end
-
end
-
1
declare :if, [:condition, :if_true, :if_false]
-
-
1
private
-
-
# This method implements the pattern of transforming a numeric value into
-
# another numeric value with the same units.
-
# It yields a number to a block to perform the operation and return a number
-
1
def numeric_transformation(value)
-
assert_type value, :Number
-
Sass::Script::Number.new(yield(value.value), value.numerator_units, value.denominator_units)
-
end
-
-
1
def _adjust(color, amount, attr, range, op, units = "")
-
assert_type color, :Color
-
assert_type amount, :Number
-
unless range.include?(amount.value)
-
raise ArgumentError.new("Amount #{amount} must be between #{range.first}#{units} and #{range.last}#{units}")
-
end
-
-
# TODO: is it worth restricting here,
-
# or should we do so in the Color constructor itself,
-
# and allow clipping in rgb() et al?
-
color.with(attr => Sass::Util.restrict(
-
color.send(attr).send(op, amount.value), range))
-
end
-
end
-
end
-
1
module Sass::Script
-
# A SassScript object representing `#{}` interpolation outside a string.
-
#
-
# @see StringInterpolation
-
1
class Interpolation < Node
-
# Interpolation in a property is of the form `before #{mid} after`.
-
#
-
# @param before [Node] The SassScript before the interpolation
-
# @param mid [Node] The SassScript within the interpolation
-
# @param after [Node] The SassScript after the interpolation
-
# @param wb [Boolean] Whether there was whitespace between `before` and `#{`
-
# @param wa [Boolean] Whether there was whitespace between `}` and `after`
-
# @param originally_text [Boolean]
-
# Whether the original format of the interpolation was plain text,
-
# not an interpolation.
-
# This is used when converting back to SassScript.
-
1
def initialize(before, mid, after, wb, wa, originally_text = false)
-
@before = before
-
@mid = mid
-
@after = after
-
@whitespace_before = wb
-
@whitespace_after = wa
-
@originally_text = originally_text
-
end
-
-
# @return [String] A human-readable s-expression representation of the interpolation
-
1
def inspect
-
"(interpolation #{@before.inspect} #{@mid.inspect} #{@after.inspect})"
-
end
-
-
# @see Node#to_sass
-
1
def to_sass(opts = {})
-
res = ""
-
res << @before.to_sass(opts) if @before
-
res << ' ' if @before && @whitespace_before
-
res << '#{' unless @originally_text
-
res << @mid.to_sass(opts)
-
res << '}' unless @originally_text
-
res << ' ' if @after && @whitespace_after
-
res << @after.to_sass(opts) if @after
-
res
-
end
-
-
# Returns the three components of the interpolation, `before`, `mid`, and `after`.
-
#
-
# @return [Array<Node>]
-
# @see #initialize
-
# @see Node#children
-
1
def children
-
[@before, @mid, @after].compact
-
end
-
-
# @see Node#deep_copy
-
1
def deep_copy
-
node = dup
-
node.instance_variable_set('@before', @before.deep_copy) if @before
-
node.instance_variable_set('@mid', @mid.deep_copy)
-
node.instance_variable_set('@after', @after.deep_copy) if @after
-
node
-
end
-
-
1
protected
-
-
# Evaluates the interpolation.
-
#
-
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
-
# @return [Sass::Script::String] The SassScript string that is the value of the interpolation
-
1
def _perform(environment)
-
res = ""
-
res << @before.perform(environment).to_s if @before
-
res << " " if @before && @whitespace_before
-
val = @mid.perform(environment)
-
res << (val.is_a?(Sass::Script::String) ? val.value : val.to_s)
-
res << " " if @after && @whitespace_after
-
res << @after.perform(environment).to_s if @after
-
opts(Sass::Script::String.new(res))
-
end
-
end
-
end
-
1
require 'sass/scss/rx'
-
-
1
module Sass
-
1
module Script
-
# The lexical analyzer for SassScript.
-
# It takes a raw string and converts it to individual tokens
-
# that are easier to parse.
-
1
class Lexer
-
1
include Sass::SCSS::RX
-
-
# A struct containing information about an individual token.
-
#
-
# `type`: \[`Symbol`\]
-
# : The type of token.
-
#
-
# `value`: \[`Object`\]
-
# : The Ruby object corresponding to the value of the token.
-
#
-
# `line`: \[`Fixnum`\]
-
# : The line of the source file on which the token appears.
-
#
-
# `offset`: \[`Fixnum`\]
-
# : The number of bytes into the line the SassScript token appeared.
-
#
-
# `pos`: \[`Fixnum`\]
-
# : The scanner position at which the SassScript token appeared.
-
1
Token = Struct.new(:type, :value, :line, :offset, :pos)
-
-
# The line number of the lexer's current position.
-
#
-
# @return [Fixnum]
-
1
attr_reader :line
-
-
# The number of bytes into the current line
-
# of the lexer's current position.
-
#
-
# @return [Fixnum]
-
1
attr_reader :offset
-
-
# A hash from operator strings to the corresponding token types.
-
1
OPERATORS = {
-
'+' => :plus,
-
'-' => :minus,
-
'*' => :times,
-
'/' => :div,
-
'%' => :mod,
-
'=' => :single_eq,
-
':' => :colon,
-
'(' => :lparen,
-
')' => :rparen,
-
',' => :comma,
-
'and' => :and,
-
'or' => :or,
-
'not' => :not,
-
'==' => :eq,
-
'!=' => :neq,
-
'>=' => :gte,
-
'<=' => :lte,
-
'>' => :gt,
-
'<' => :lt,
-
'#{' => :begin_interpolation,
-
'}' => :end_interpolation,
-
';' => :semicolon,
-
'{' => :lcurly,
-
}
-
-
24
OPERATORS_REVERSE = Sass::Util.map_hash(OPERATORS) {|k, v| [v, k]}
-
-
24
TOKEN_NAMES = Sass::Util.map_hash(OPERATORS_REVERSE) {|k, v| [k, v.inspect]}.merge({
-
:const => "variable (e.g. $foo)",
-
:ident => "identifier (e.g. middle)",
-
:bool => "boolean (e.g. true, false)",
-
})
-
-
# A list of operator strings ordered with longer names first
-
# so that `>` and `<` don't clobber `>=` and `<=`.
-
24
OP_NAMES = OPERATORS.keys.sort_by {|o| -o.size}
-
-
# A sub-list of {OP_NAMES} that only includes operators
-
# with identifier names.
-
24
IDENT_OP_NAMES = OP_NAMES.select {|k, v| k =~ /^\w+/}
-
-
# A hash of regular expressions that are used for tokenizing.
-
1
REGULAR_EXPRESSIONS = {
-
:whitespace => /\s+/,
-
:comment => COMMENT,
-
:single_line_comment => SINGLE_LINE_COMMENT,
-
:variable => /(\$)(#{IDENT})/,
-
:ident => /(#{IDENT})(\()?/,
-
:number => /(-)?(?:(\d*\.\d+)|(\d+))([a-zA-Z%]+)?/,
-
:color => HEXCOLOR,
-
:bool => /(true|false)\b/,
-
3
:ident_op => %r{(#{Regexp.union(*IDENT_OP_NAMES.map{|s| Regexp.new(Regexp.escape(s) + "(?!#{NMCHAR}|\Z)")})})},
-
:op => %r{(#{Regexp.union(*OP_NAMES)})},
-
}
-
-
1
class << self
-
1
private
-
1
def string_re(open, close)
-
4
/#{open}((?:\\.|\#(?!\{)|[^#{close}\\#])*)(#{close}|#\{)/
-
end
-
end
-
-
# A hash of regular expressions that are used for tokenizing strings.
-
#
-
# The key is a `[Symbol, Boolean]` pair.
-
# The symbol represents which style of quotation to use,
-
# while the boolean represents whether or not the string
-
# is following an interpolated segment.
-
1
STRING_REGULAR_EXPRESSIONS = {
-
[:double, false] => string_re('"', '"'),
-
[:single, false] => string_re("'", "'"),
-
[:double, true] => string_re('', '"'),
-
[:single, true] => string_re('', "'"),
-
[:uri, false] => /url\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/,
-
[:uri, true] => /(#{URLCHAR}*?)(#{W}\)|#\{)/,
-
}
-
-
# @param str [String, StringScanner] The source text to lex
-
# @param line [Fixnum] The line on which the SassScript appears.
-
# Used for error reporting
-
# @param offset [Fixnum] The number of characters in on which the SassScript appears.
-
# Used for error reporting
-
# @param options [{Symbol => Object}] An options hash;
-
# see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
-
1
def initialize(str, line, offset, options)
-
@scanner = str.is_a?(StringScanner) ? str : Sass::Util::MultibyteStringScanner.new(str)
-
@line = line
-
@offset = offset
-
@options = options
-
@interpolation_stack = []
-
@prev = nil
-
end
-
-
# Moves the lexer forward one token.
-
#
-
# @return [Token] The token that was moved past
-
1
def next
-
@tok ||= read_token
-
@tok, tok = nil, @tok
-
@prev = tok
-
return tok
-
end
-
-
# Returns whether or not there's whitespace before the next token.
-
#
-
# @return [Boolean]
-
1
def whitespace?(tok = @tok)
-
if tok
-
@scanner.string[0...tok.pos] =~ /\s\Z/
-
else
-
@scanner.string[@scanner.pos, 1] =~ /^\s/ ||
-
@scanner.string[@scanner.pos - 1, 1] =~ /\s\Z/
-
end
-
end
-
-
# Returns the next token without moving the lexer forward.
-
#
-
# @return [Token] The next token
-
1
def peek
-
@tok ||= read_token
-
end
-
-
# Rewinds the underlying StringScanner
-
# to before the token returned by \{#peek}.
-
1
def unpeek!
-
@scanner.pos = @tok.pos if @tok
-
end
-
-
# @return [Boolean] Whether or not there's more source text to lex.
-
1
def done?
-
whitespace unless after_interpolation? && @interpolation_stack.last
-
@scanner.eos? && @tok.nil?
-
end
-
-
# @return [Boolean] Whether or not the last token lexed was `:end_interpolation`.
-
1
def after_interpolation?
-
@prev && @prev.type == :end_interpolation
-
end
-
-
# Raise an error to the effect that `name` was expected in the input stream
-
# and wasn't found.
-
#
-
# This calls \{#unpeek!} to rewind the scanner to immediately after
-
# the last returned token.
-
#
-
# @param name [String] The name of the entity that was expected but not found
-
# @raise [Sass::SyntaxError]
-
1
def expected!(name)
-
unpeek!
-
Sass::SCSS::Parser.expected(@scanner, name, @line)
-
end
-
-
# Records all non-comment text the lexer consumes within the block
-
# and returns it as a string.
-
#
-
# @yield A block in which text is recorded
-
# @return [String]
-
1
def str
-
old_pos = @tok ? @tok.pos : @scanner.pos
-
yield
-
new_pos = @tok ? @tok.pos : @scanner.pos
-
@scanner.string[old_pos...new_pos]
-
end
-
-
1
private
-
-
1
def read_token
-
return if done?
-
return unless value = token
-
type, val, size = value
-
size ||= @scanner.matched_size
-
-
val.line = @line if val.is_a?(Script::Node)
-
Token.new(type, val, @line,
-
current_position - size, @scanner.pos - size)
-
end
-
-
1
def whitespace
-
nil while scan(REGULAR_EXPRESSIONS[:whitespace]) ||
-
scan(REGULAR_EXPRESSIONS[:comment]) ||
-
scan(REGULAR_EXPRESSIONS[:single_line_comment])
-
end
-
-
1
def token
-
if after_interpolation? && (interp_type = @interpolation_stack.pop)
-
return string(interp_type, true)
-
end
-
-
variable || string(:double, false) || string(:single, false) || number ||
-
color || bool || string(:uri, false) || raw(UNICODERANGE) ||
-
special_fun || special_val || ident_op || ident || op
-
end
-
-
1
def variable
-
_variable(REGULAR_EXPRESSIONS[:variable])
-
end
-
-
1
def _variable(rx)
-
line = @line
-
offset = @offset
-
return unless scan(rx)
-
-
[:const, @scanner[2]]
-
end
-
-
1
def ident
-
return unless scan(REGULAR_EXPRESSIONS[:ident])
-
[@scanner[2] ? :funcall : :ident, @scanner[1]]
-
end
-
-
1
def string(re, open)
-
return unless scan(STRING_REGULAR_EXPRESSIONS[[re, open]])
-
if @scanner[2] == '#{' #'
-
@scanner.pos -= 2 # Don't actually consume the #{
-
@interpolation_stack << re
-
end
-
str =
-
if re == :uri
-
Script::String.new("#{'url(' unless open}#{@scanner[1]}#{')' unless @scanner[2] == '#{'}")
-
else
-
Script::String.new(@scanner[1].gsub(/\\(['"]|\#\{)/, '\1'), :string)
-
end
-
[:string, str]
-
end
-
-
1
def number
-
return unless scan(REGULAR_EXPRESSIONS[:number])
-
value = @scanner[2] ? @scanner[2].to_f : @scanner[3].to_i
-
value = -value if @scanner[1]
-
[:number, Script::Number.new(value, Array(@scanner[4]))]
-
end
-
-
1
def color
-
return unless s = scan(REGULAR_EXPRESSIONS[:color])
-
raise Sass::SyntaxError.new(<<MESSAGE.rstrip) unless s.size == 4 || s.size == 7
-
Colors must have either three or six digits: '#{s}'
-
MESSAGE
-
value = s.scan(/^#(..?)(..?)(..?)$/).first.
-
map {|num| num.ljust(2, num).to_i(16)}
-
[:color, Script::Color.new(value)]
-
end
-
-
1
def bool
-
return unless s = scan(REGULAR_EXPRESSIONS[:bool])
-
[:bool, Script::Bool.new(s == 'true')]
-
end
-
-
1
def special_fun
-
return unless str1 = scan(/((-[\w-]+-)?(calc|element)|expression|progid:[a-z\.]*)\(/i)
-
str2, _ = Sass::Shared.balance(@scanner, ?(, ?), 1)
-
c = str2.count("\n")
-
old_line = @line
-
old_offset = @offset
-
@line += c
-
@offset = (c == 0 ? @offset + str2.size : str2[/\n(.*)/, 1].size)
-
[:special_fun,
-
Sass::Util.merge_adjacent_strings(
-
[str1] + Sass::Engine.parse_interp(str2, old_line, old_offset, @options)),
-
str1.size + str2.size]
-
end
-
-
1
def special_val
-
return unless scan(/!important/i)
-
[:string, Script::String.new("!important")]
-
end
-
-
1
def ident_op
-
return unless op = scan(REGULAR_EXPRESSIONS[:ident_op])
-
[OPERATORS[op]]
-
end
-
-
1
def op
-
return unless op = scan(REGULAR_EXPRESSIONS[:op])
-
@interpolation_stack << nil if op == :begin_interpolation
-
[OPERATORS[op]]
-
end
-
-
1
def raw(rx)
-
return unless val = scan(rx)
-
[:raw, val]
-
end
-
-
1
def scan(re)
-
return unless str = @scanner.scan(re)
-
c = str.count("\n")
-
@line += c
-
@offset = (c == 0 ? @offset + str.size : str[/\n(.*)/, 1].size)
-
str
-
end
-
-
1
def current_position
-
@offset + 1
-
end
-
end
-
end
-
end
-
1
module Sass::Script
-
# A SassScript object representing a CSS list.
-
# This includes both comma-separated lists and space-separated lists.
-
1
class List < Literal
-
# The Ruby array containing the contents of the list.
-
#
-
# @return [Array<Literal>]
-
1
attr_reader :value
-
1
alias_method :children, :value
-
1
alias_method :to_a, :value
-
-
# The operator separating the values of the list.
-
# Either `:comma` or `:space`.
-
#
-
# @return [Symbol]
-
1
attr_reader :separator
-
-
# Creates a new list.
-
#
-
# @param value [Array<Literal>] See \{#value}
-
# @param separator [String] See \{#separator}
-
1
def initialize(value, separator)
-
super(value)
-
@separator = separator
-
end
-
-
# @see Node#deep_copy
-
1
def deep_copy
-
node = dup
-
node.instance_variable_set('@value', value.map {|c| c.deep_copy})
-
node
-
end
-
-
# @see Node#eq
-
1
def eq(other)
-
Sass::Script::Bool.new(
-
self.class == other.class && self.value == other.value &&
-
self.separator == other.separator)
-
end
-
-
# @see Node#to_s
-
1
def to_s(opts = {})
-
raise Sass::SyntaxError.new("() isn't a valid CSS value.") if value.empty?
-
return value.reject {|e| e.is_a?(List) && e.value.empty?}.map {|e| e.to_s(opts)}.join(sep_str)
-
end
-
-
# @see Node#to_sass
-
1
def to_sass(opts = {})
-
return "()" if value.empty?
-
precedence = Sass::Script::Parser.precedence_of(separator)
-
value.map do |v|
-
if v.is_a?(List) && Sass::Script::Parser.precedence_of(v.separator) <= precedence
-
"(#{v.to_sass(opts)})"
-
else
-
v.to_sass(opts)
-
end
-
end.join(sep_str(nil))
-
end
-
-
# @see Node#inspect
-
1
def inspect
-
"(#{to_sass})"
-
end
-
-
1
protected
-
-
# @see Node#_perform
-
1
def _perform(environment)
-
list = Sass::Script::List.new(
-
value.map {|e| e.perform(environment)},
-
separator)
-
list.options = self.options
-
list
-
end
-
-
1
private
-
-
1
def sep_str(opts = self.options)
-
return ' ' if separator == :space
-
return ',' if opts && opts[:style] == :compressed
-
return ', '
-
end
-
end
-
end
-
1
module Sass::Script
-
# The abstract superclass for SassScript objects.
-
#
-
# Many of these methods, especially the ones that correspond to SassScript operations,
-
# are designed to be overridden by subclasses which may change the semantics somewhat.
-
# The operations listed here are just the defaults.
-
1
class Literal < Node
-
1
require 'sass/script/string'
-
1
require 'sass/script/number'
-
1
require 'sass/script/color'
-
1
require 'sass/script/bool'
-
1
require 'sass/script/list'
-
-
# Returns the Ruby value of the literal.
-
# The type of this value varies based on the subclass.
-
#
-
# @return [Object]
-
1
attr_reader :value
-
-
# Creates a new literal.
-
#
-
# @param value [Object] The object for \{#value}
-
1
def initialize(value = nil)
-
@value = value
-
super()
-
end
-
-
# Returns an empty array.
-
#
-
# @return [Array<Node>] empty
-
# @see Node#children
-
1
def children
-
[]
-
end
-
-
# @see Node#deep_copy
-
1
def deep_copy
-
dup
-
end
-
-
# Returns the options hash for this node.
-
#
-
# @return [{Symbol => Object}]
-
# @raise [Sass::SyntaxError] if the options hash hasn't been set.
-
# This should only happen when the literal was created
-
# outside of the parser and \{#to\_s} was called on it
-
1
def options
-
opts = super
-
return opts if opts
-
raise Sass::SyntaxError.new(<<MSG)
-
The #options attribute is not set on this #{self.class}.
-
This error is probably occurring because #to_s was called
-
on this literal within a custom Sass function without first
-
setting the #option attribute.
-
MSG
-
end
-
-
# The SassScript `==` operation.
-
# **Note that this returns a {Sass::Script::Bool} object,
-
# not a Ruby boolean**.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Bool] True if this literal is the same as the other,
-
# false otherwise
-
1
def eq(other)
-
Sass::Script::Bool.new(self.class == other.class && self.value == other.value)
-
end
-
-
# The SassScript `!=` operation.
-
# **Note that this returns a {Sass::Script::Bool} object,
-
# not a Ruby boolean**.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Bool] False if this literal is the same as the other,
-
# true otherwise
-
1
def neq(other)
-
Sass::Script::Bool.new(!eq(other).to_bool)
-
end
-
-
# The SassScript `==` operation.
-
# **Note that this returns a {Sass::Script::Bool} object,
-
# not a Ruby boolean**.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Bool] True if this literal is the same as the other,
-
# false otherwise
-
1
def unary_not
-
Sass::Script::Bool.new(!to_bool)
-
end
-
-
# The SassScript default operation (e.g. `$a $b`, `"foo" "bar"`).
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Script::String] A string containing both literals
-
# separated by a space
-
1
def space(other)
-
Sass::Script::String.new("#{self.to_s} #{other.to_s}")
-
end
-
-
# The SassScript `,` operation (e.g. `$a, $b`, `"foo", "bar"`).
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Script::String] A string containing both literals
-
# separated by `", "`
-
1
def comma(other)
-
Sass::Script::String.new("#{self.to_s},#{' ' unless options[:style] == :compressed}#{other.to_s}")
-
end
-
-
# The SassScript `=` operation
-
# (used for proprietary MS syntax like `alpha(opacity=20)`).
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Script::String] A string containing both literals
-
# separated by `"="`
-
1
def single_eq(other)
-
Sass::Script::String.new("#{self.to_s}=#{other.to_s}")
-
end
-
-
# The SassScript `+` operation.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Script::String] A string containing both literals
-
# without any separation
-
1
def plus(other)
-
if other.is_a?(Sass::Script::String)
-
return Sass::Script::String.new(self.to_s + other.value, other.type)
-
end
-
Sass::Script::String.new(self.to_s + other.to_s)
-
end
-
-
# The SassScript `-` operation.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Script::String] A string containing both literals
-
# separated by `"-"`
-
1
def minus(other)
-
Sass::Script::String.new("#{self.to_s}-#{other.to_s}")
-
end
-
-
# The SassScript `/` operation.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Script::String] A string containing both literals
-
# separated by `"/"`
-
1
def div(other)
-
Sass::Script::String.new("#{self.to_s}/#{other.to_s}")
-
end
-
-
# The SassScript unary `+` operation (e.g. `+$a`).
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Script::String] A string containing the literal
-
# preceded by `"+"`
-
1
def unary_plus
-
Sass::Script::String.new("+#{self.to_s}")
-
end
-
-
# The SassScript unary `-` operation (e.g. `-$a`).
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Script::String] A string containing the literal
-
# preceded by `"-"`
-
1
def unary_minus
-
Sass::Script::String.new("-#{self.to_s}")
-
end
-
-
# The SassScript unary `/` operation (e.g. `/$a`).
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Script::String] A string containing the literal
-
# preceded by `"/"`
-
1
def unary_div
-
Sass::Script::String.new("/#{self.to_s}")
-
end
-
-
# @return [String] A readable representation of the literal
-
1
def inspect
-
value.inspect
-
end
-
-
# @return [Boolean] `true` (the Ruby boolean value)
-
1
def to_bool
-
true
-
end
-
-
# Compares this object with another.
-
#
-
# @param other [Object] The object to compare with
-
# @return [Boolean] Whether or not this literal is equivalent to `other`
-
1
def ==(other)
-
eq(other).to_bool
-
end
-
-
# @return [Fixnum] The integer value of this literal
-
# @raise [Sass::SyntaxError] if this literal isn't an integer
-
1
def to_i
-
raise Sass::SyntaxError.new("#{self.inspect} is not an integer.")
-
end
-
-
# @raise [Sass::SyntaxError] if this literal isn't an integer
-
1
def assert_int!; to_i; end
-
-
# Returns the value of this literal as a list.
-
# Single literals are considered the same as single-element lists.
-
#
-
# @return [Array<Literal>] The of this literal as a list
-
1
def to_a
-
[self]
-
end
-
-
# Returns the string representation of this literal
-
# as it would be output to the CSS document.
-
#
-
# @return [String]
-
1
def to_s(opts = {})
-
raise Sass::SyntaxError.new("[BUG] All subclasses of Sass::Literal must implement #to_s.")
-
end
-
1
alias_method :to_sass, :to_s
-
-
1
protected
-
-
# Evaluates the literal.
-
#
-
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
-
# @return [Literal] This literal
-
1
def _perform(environment)
-
self
-
end
-
end
-
end
-
1
module Sass::Script
-
# The abstract superclass for SassScript parse tree nodes.
-
#
-
# Use \{#perform} to evaluate a parse tree.
-
1
class Node
-
# The options hash for this node.
-
#
-
# @return [{Symbol => Object}]
-
1
attr_reader :options
-
-
# The line of the document on which this node appeared.
-
#
-
# @return [Fixnum]
-
1
attr_accessor :line
-
-
# Sets the options hash for this node,
-
# as well as for all child nodes.
-
# See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
-
#
-
# @param options [{Symbol => Object}] The options
-
1
def options=(options)
-
@options = options
-
children.each do |c|
-
if c.is_a? Hash
-
c.values.each {|v| v.options = options }
-
else
-
c.options = options
-
end
-
end
-
end
-
-
# Evaluates the node.
-
#
-
# \{#perform} shouldn't be overridden directly;
-
# instead, override \{#\_perform}.
-
#
-
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
-
# @return [Literal] The SassScript object that is the value of the SassScript
-
1
def perform(environment)
-
_perform(environment)
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace(:line => line)
-
raise e
-
end
-
-
# Returns all child nodes of this node.
-
#
-
# @return [Array<Node>]
-
1
def children
-
Sass::Util.abstract(self)
-
end
-
-
# Returns the text of this SassScript expression.
-
#
-
# @return [String]
-
1
def to_sass(opts = {})
-
Sass::Util.abstract(self)
-
end
-
-
# Returns a deep clone of this node.
-
# The child nodes are cloned, but options are not.
-
#
-
# @return [Node]
-
1
def deep_copy
-
Sass::Util.abstract(self)
-
end
-
-
1
protected
-
-
# Converts underscores to dashes if the :dasherize option is set.
-
1
def dasherize(s, opts)
-
if opts[:dasherize]
-
s.gsub(/_/,'-')
-
else
-
s
-
end
-
end
-
-
# Evaluates this node.
-
# Note that all {Literal} objects created within this method
-
# should have their \{#options} attribute set, probably via \{#opts}.
-
#
-
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
-
# @return [Literal] The SassScript object that is the value of the SassScript
-
# @see #perform
-
1
def _perform(environment)
-
Sass::Util.abstract(self)
-
end
-
-
# Sets the \{#options} field on the given literal and returns it
-
#
-
# @param literal [Literal]
-
# @return [Literal]
-
1
def opts(literal)
-
literal.options = options
-
literal
-
end
-
end
-
end
-
1
require 'sass/script/literal'
-
-
1
module Sass::Script
-
# A SassScript object representing a number.
-
# SassScript numbers can have decimal values,
-
# and can also have units.
-
# For example, `12`, `1px`, and `10.45em`
-
# are all valid values.
-
#
-
# Numbers can also have more complex units, such as `1px*em/in`.
-
# These cannot be inputted directly in Sass code at the moment.
-
1
class Number < Literal
-
# The Ruby value of the number.
-
#
-
# @return [Numeric]
-
1
attr_reader :value
-
-
# A list of units in the numerator of the number.
-
# For example, `1px*em/in*cm` would return `["px", "em"]`
-
# @return [Array<String>]
-
1
attr_reader :numerator_units
-
-
# A list of units in the denominator of the number.
-
# For example, `1px*em/in*cm` would return `["in", "cm"]`
-
# @return [Array<String>]
-
1
attr_reader :denominator_units
-
-
# The original representation of this number.
-
# For example, although the result of `1px/2px` is `0.5`,
-
# the value of `#original` is `"1px/2px"`.
-
#
-
# This is only non-nil when the original value should be used as the CSS value,
-
# as in `font: 1px/2px`.
-
#
-
# @return [Boolean, nil]
-
1
attr_accessor :original
-
-
1
def self.precision
-
@precision ||= 3
-
end
-
-
# Sets the number of digits of precision
-
# For example, if this is `3`,
-
# `3.1415926` will be printed as `3.142`.
-
1
def self.precision=(digits)
-
@precision = digits.round
-
@precision_factor = 10.0**@precision
-
end
-
-
# the precision factor used in numeric output
-
# it is derived from the `precision` method.
-
1
def self.precision_factor
-
@precision_factor ||= 10.0**precision
-
end
-
-
# Handles the deprecation warning for the PRECISION constant
-
# This can be removed in 3.2.
-
1
def self.const_missing(const)
-
if const == :PRECISION
-
Sass::Util.sass_warn("Sass::Script::Number::PRECISION is deprecated and will be removed in a future release. Use Sass::Script::Number.precision_factor instead.")
-
const_set(:PRECISION, self.precision_factor)
-
else
-
super
-
end
-
end
-
-
# Used so we don't allocate two new arrays for each new number.
-
1
NO_UNITS = []
-
-
# @param value [Numeric] The value of the number
-
# @param numerator_units [Array<String>] See \{#numerator\_units}
-
# @param denominator_units [Array<String>] See \{#denominator\_units}
-
1
def initialize(value, numerator_units = NO_UNITS, denominator_units = NO_UNITS)
-
super(value)
-
@numerator_units = numerator_units
-
@denominator_units = denominator_units
-
normalize!
-
end
-
-
# The SassScript `+` operation.
-
# Its functionality depends on the type of its argument:
-
#
-
# {Number}
-
# : Adds the two numbers together, converting units if possible.
-
#
-
# {Color}
-
# : Adds this number to each of the RGB color channels.
-
#
-
# {Literal}
-
# : See {Literal#plus}.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Literal] The result of the operation
-
# @raise [Sass::UnitConversionError] if `other` is a number with incompatible units
-
1
def plus(other)
-
if other.is_a? Number
-
operate(other, :+)
-
elsif other.is_a?(Color)
-
other.plus(self)
-
else
-
super
-
end
-
end
-
-
# The SassScript binary `-` operation (e.g. `$a - $b`).
-
# Its functionality depends on the type of its argument:
-
#
-
# {Number}
-
# : Subtracts this number from the other, converting units if possible.
-
#
-
# {Literal}
-
# : See {Literal#minus}.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Literal] The result of the operation
-
# @raise [Sass::UnitConversionError] if `other` is a number with incompatible units
-
1
def minus(other)
-
if other.is_a? Number
-
operate(other, :-)
-
else
-
super
-
end
-
end
-
-
# The SassScript unary `+` operation (e.g. `+$a`).
-
#
-
# @return [Number] The value of this number
-
1
def unary_plus
-
self
-
end
-
-
# The SassScript unary `-` operation (e.g. `-$a`).
-
#
-
# @return [Number] The negative value of this number
-
1
def unary_minus
-
Number.new(-value, @numerator_units, @denominator_units)
-
end
-
-
# The SassScript `*` operation.
-
# Its functionality depends on the type of its argument:
-
#
-
# {Number}
-
# : Multiplies the two numbers together, converting units appropriately.
-
#
-
# {Color}
-
# : Multiplies each of the RGB color channels by this number.
-
#
-
# @param other [Number, Color] The right-hand side of the operator
-
# @return [Number, Color] The result of the operation
-
# @raise [NoMethodError] if `other` is an invalid type
-
1
def times(other)
-
if other.is_a? Number
-
operate(other, :*)
-
elsif other.is_a? Color
-
other.times(self)
-
else
-
raise NoMethodError.new(nil, :times)
-
end
-
end
-
-
# The SassScript `/` operation.
-
# Its functionality depends on the type of its argument:
-
#
-
# {Number}
-
# : Divides this number by the other, converting units appropriately.
-
#
-
# {Literal}
-
# : See {Literal#div}.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Literal] The result of the operation
-
1
def div(other)
-
if other.is_a? Number
-
res = operate(other, :/)
-
if self.original && other.original
-
res.original = "#{self.original}/#{other.original}"
-
end
-
res
-
else
-
super
-
end
-
end
-
-
# The SassScript `%` operation.
-
#
-
# @param other [Number] The right-hand side of the operator
-
# @return [Number] This number modulo the other
-
# @raise [NoMethodError] if `other` is an invalid type
-
# @raise [Sass::UnitConversionError] if `other` has any units
-
1
def mod(other)
-
if other.is_a?(Number)
-
unless other.unitless?
-
raise Sass::UnitConversionError.new("Cannot modulo by a number with units: #{other.inspect}.")
-
end
-
operate(other, :%)
-
else
-
raise NoMethodError.new(nil, :mod)
-
end
-
end
-
-
# The SassScript `==` operation.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Boolean] Whether this number is equal to the other object
-
1
def eq(other)
-
return Sass::Script::Bool.new(false) unless other.is_a?(Sass::Script::Number)
-
this = self
-
begin
-
if unitless?
-
this = this.coerce(other.numerator_units, other.denominator_units)
-
else
-
other = other.coerce(@numerator_units, @denominator_units)
-
end
-
rescue Sass::UnitConversionError
-
return Sass::Script::Bool.new(false)
-
end
-
-
Sass::Script::Bool.new(this.value == other.value)
-
end
-
-
# The SassScript `>` operation.
-
#
-
# @param other [Number] The right-hand side of the operator
-
# @return [Boolean] Whether this number is greater than the other
-
# @raise [NoMethodError] if `other` is an invalid type
-
1
def gt(other)
-
raise NoMethodError.new(nil, :gt) unless other.is_a?(Number)
-
operate(other, :>)
-
end
-
-
# The SassScript `>=` operation.
-
#
-
# @param other [Number] The right-hand side of the operator
-
# @return [Boolean] Whether this number is greater than or equal to the other
-
# @raise [NoMethodError] if `other` is an invalid type
-
1
def gte(other)
-
raise NoMethodError.new(nil, :gte) unless other.is_a?(Number)
-
operate(other, :>=)
-
end
-
-
# The SassScript `<` operation.
-
#
-
# @param other [Number] The right-hand side of the operator
-
# @return [Boolean] Whether this number is less than the other
-
# @raise [NoMethodError] if `other` is an invalid type
-
1
def lt(other)
-
raise NoMethodError.new(nil, :lt) unless other.is_a?(Number)
-
operate(other, :<)
-
end
-
-
# The SassScript `<=` operation.
-
#
-
# @param other [Number] The right-hand side of the operator
-
# @return [Boolean] Whether this number is less than or equal to the other
-
# @raise [NoMethodError] if `other` is an invalid type
-
1
def lte(other)
-
raise NoMethodError.new(nil, :lte) unless other.is_a?(Number)
-
operate(other, :<=)
-
end
-
-
# @return [String] The CSS representation of this number
-
# @raise [Sass::SyntaxError] if this number has units that can't be used in CSS
-
# (e.g. `px*in`)
-
1
def to_s(opts = {})
-
return original if original
-
raise Sass::SyntaxError.new("#{inspect} isn't a valid CSS value.") unless legal_units?
-
inspect
-
end
-
-
# Returns a readable representation of this number.
-
#
-
# This representation is valid CSS (and valid SassScript)
-
# as long as there is only one unit.
-
#
-
# @return [String] The representation
-
1
def inspect(opts = {})
-
value = self.class.round(self.value)
-
unitless? ? value.to_s : "#{value}#{unit_str}"
-
end
-
1
alias_method :to_sass, :inspect
-
-
# @return [Fixnum] The integer value of the number
-
# @raise [Sass::SyntaxError] if the number isn't an integer
-
1
def to_i
-
super unless int?
-
return value
-
end
-
-
# @return [Boolean] Whether or not this number is an integer.
-
1
def int?
-
value % 1 == 0.0
-
end
-
-
# @return [Boolean] Whether or not this number has no units.
-
1
def unitless?
-
@numerator_units.empty? && @denominator_units.empty?
-
end
-
-
# @return [Boolean] Whether or not this number has units that can be represented in CSS
-
# (that is, zero or one \{#numerator\_units}).
-
1
def legal_units?
-
(@numerator_units.empty? || @numerator_units.size == 1) && @denominator_units.empty?
-
end
-
-
# Returns this number converted to other units.
-
# The conversion takes into account the relationship between e.g. mm and cm,
-
# as well as between e.g. in and cm.
-
#
-
# If this number has no units, it will simply return itself
-
# with the given units.
-
#
-
# An incompatible coercion, e.g. between px and cm, will raise an error.
-
#
-
# @param num_units [Array<String>] The numerator units to coerce this number into.
-
# See {\#numerator\_units}
-
# @param den_units [Array<String>] The denominator units to coerce this number into.
-
# See {\#denominator\_units}
-
# @return [Number] The number with the new units
-
# @raise [Sass::UnitConversionError] if the given units are incompatible with the number's
-
# current units
-
1
def coerce(num_units, den_units)
-
Number.new(if unitless?
-
self.value
-
else
-
self.value * coercion_factor(@numerator_units, num_units) /
-
coercion_factor(@denominator_units, den_units)
-
end, num_units, den_units)
-
end
-
-
# @param other [Number] A number to decide if it can be compared with this number.
-
# @return [Boolean] Whether or not this number can be compared with the other.
-
1
def comparable_to?(other)
-
begin
-
operate(other, :+)
-
true
-
rescue Sass::UnitConversionError
-
false
-
end
-
end
-
-
# Returns a human readable representation of the units in this number.
-
# For complex units this takes the form of:
-
# numerator_unit1 * numerator_unit2 / denominator_unit1 * denominator_unit2
-
# @return [String] a string that represents the units in this number
-
1
def unit_str
-
rv = @numerator_units.sort.join("*")
-
if @denominator_units.any?
-
rv << "/"
-
rv << @denominator_units.sort.join("*")
-
end
-
rv
-
end
-
-
1
private
-
-
# @private
-
1
def self.round(num)
-
if num.is_a?(Float) && (num.infinite? || num.nan?)
-
num
-
elsif num % 1 == 0.0
-
num.to_i
-
else
-
((num * self.precision_factor).round / self.precision_factor).to_f
-
end
-
end
-
-
1
OPERATIONS = [:+, :-, :<=, :<, :>, :>=]
-
-
1
def operate(other, operation)
-
this = self
-
if OPERATIONS.include?(operation)
-
if unitless?
-
this = this.coerce(other.numerator_units, other.denominator_units)
-
else
-
other = other.coerce(@numerator_units, @denominator_units)
-
end
-
end
-
# avoid integer division
-
value = (:/ == operation) ? this.value.to_f : this.value
-
result = value.send(operation, other.value)
-
-
if result.is_a?(Numeric)
-
Number.new(result, *compute_units(this, other, operation))
-
else # Boolean op
-
Bool.new(result)
-
end
-
end
-
-
1
def coercion_factor(from_units, to_units)
-
# get a list of unmatched units
-
from_units, to_units = sans_common_units(from_units, to_units)
-
-
if from_units.size != to_units.size || !convertable?(from_units | to_units)
-
raise Sass::UnitConversionError.new("Incompatible units: '#{from_units.join('*')}' and '#{to_units.join('*')}'.")
-
end
-
-
from_units.zip(to_units).inject(1) {|m,p| m * conversion_factor(p[0], p[1]) }
-
end
-
-
1
def compute_units(this, other, operation)
-
case operation
-
when :*
-
[this.numerator_units + other.numerator_units, this.denominator_units + other.denominator_units]
-
when :/
-
[this.numerator_units + other.denominator_units, this.denominator_units + other.numerator_units]
-
else
-
[this.numerator_units, this.denominator_units]
-
end
-
end
-
-
1
def normalize!
-
return if unitless?
-
@numerator_units, @denominator_units = sans_common_units(@numerator_units, @denominator_units)
-
-
@denominator_units.each_with_index do |d, i|
-
if convertable?(d) && (u = @numerator_units.detect(&method(:convertable?)))
-
@value /= conversion_factor(d, u)
-
@denominator_units.delete_at(i)
-
@numerator_units.delete_at(@numerator_units.index(u))
-
end
-
end
-
end
-
-
# A hash of unit names to their index in the conversion table
-
1
CONVERTABLE_UNITS = {"in" => 0, "cm" => 1, "pc" => 2, "mm" => 3, "pt" => 4}
-
1
CONVERSION_TABLE = [[ 1, 2.54, 6, 25.4, 72 ], # in
-
[ nil, 1, 2.36220473, 10, 28.3464567], # cm
-
[ nil, nil, 1, 4.23333333, 12 ], # pc
-
[ nil, nil, nil, 1, 2.83464567], # mm
-
[ nil, nil, nil, nil, 1 ]] # pt
-
-
1
def conversion_factor(from_unit, to_unit)
-
res = CONVERSION_TABLE[CONVERTABLE_UNITS[from_unit]][CONVERTABLE_UNITS[to_unit]]
-
return 1.0 / conversion_factor(to_unit, from_unit) if res.nil?
-
res
-
end
-
-
1
def convertable?(units)
-
Array(units).all? {|u| CONVERTABLE_UNITS.include?(u)}
-
end
-
-
1
def sans_common_units(units1, units2)
-
units2 = units2.dup
-
# Can't just use -, because we want px*px to coerce properly to px*mm
-
return units1.map do |u|
-
next u unless j = units2.index(u)
-
units2.delete_at(j)
-
nil
-
end.compact, units2
-
end
-
end
-
end
-
1
require 'set'
-
1
require 'sass/script/string'
-
1
require 'sass/script/number'
-
1
require 'sass/script/color'
-
1
require 'sass/script/functions'
-
1
require 'sass/script/unary_operation'
-
1
require 'sass/script/interpolation'
-
1
require 'sass/script/string_interpolation'
-
-
1
module Sass::Script
-
# A SassScript parse node representing a binary operation,
-
# such as `$a + $b` or `"foo" + 1`.
-
1
class Operation < Node
-
1
attr_reader :operand1
-
1
attr_reader :operand2
-
1
attr_reader :operator
-
-
# @param operand1 [Script::Node] The parse-tree node
-
# for the right-hand side of the operator
-
# @param operand2 [Script::Node] The parse-tree node
-
# for the left-hand side of the operator
-
# @param operator [Symbol] The operator to perform.
-
# This should be one of the binary operator names in {Lexer::OPERATORS}
-
1
def initialize(operand1, operand2, operator)
-
@operand1 = operand1
-
@operand2 = operand2
-
@operator = operator
-
super()
-
end
-
-
# @return [String] A human-readable s-expression representation of the operation
-
1
def inspect
-
"(#{@operator.inspect} #{@operand1.inspect} #{@operand2.inspect})"
-
end
-
-
# @see Node#to_sass
-
1
def to_sass(opts = {})
-
pred = Sass::Script::Parser.precedence_of(@operator)
-
o1 = operand_to_sass @operand1, :left, opts
-
o2 = operand_to_sass @operand2, :right, opts
-
sep =
-
case @operator
-
when :comma; ", "
-
when :space; " "
-
else; " #{Lexer::OPERATORS_REVERSE[@operator]} "
-
end
-
"#{o1}#{sep}#{o2}"
-
end
-
-
# Returns the operands for this operation.
-
#
-
# @return [Array<Node>]
-
# @see Node#children
-
1
def children
-
[@operand1, @operand2]
-
end
-
-
# @see Node#deep_copy
-
1
def deep_copy
-
node = dup
-
node.instance_variable_set('@operand1', @operand1.deep_copy)
-
node.instance_variable_set('@operand2', @operand2.deep_copy)
-
node
-
end
-
-
1
protected
-
-
# Evaluates the operation.
-
#
-
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
-
# @return [Literal] The SassScript object that is the value of the operation
-
# @raise [Sass::SyntaxError] if the operation is undefined for the operands
-
1
def _perform(environment)
-
literal1 = @operand1.perform(environment)
-
-
# Special-case :and and :or to support short-circuiting.
-
if @operator == :and
-
return literal1.to_bool ? @operand2.perform(environment) : literal1
-
elsif @operator == :or
-
return literal1.to_bool ? literal1 : @operand2.perform(environment)
-
end
-
-
literal2 = @operand2.perform(environment)
-
-
begin
-
opts(literal1.send(@operator, literal2))
-
rescue NoMethodError => e
-
raise e unless e.name.to_s == @operator.to_s
-
raise Sass::SyntaxError.new("Undefined operation: \"#{literal1} #{@operator} #{literal2}\".")
-
end
-
end
-
-
1
private
-
-
1
def operand_to_sass(op, side, opts)
-
return "(#{op.to_sass(opts)})" if op.is_a?(List)
-
return op.to_sass(opts) unless op.is_a?(Operation)
-
-
pred = Sass::Script::Parser.precedence_of(@operator)
-
sub_pred = Sass::Script::Parser.precedence_of(op.operator)
-
assoc = Sass::Script::Parser.associative?(@operator)
-
return "(#{op.to_sass(opts)})" if sub_pred < pred ||
-
(side == :right && sub_pred == pred && !assoc)
-
op.to_sass(opts)
-
end
-
end
-
end
-
1
require 'sass/script/lexer'
-
-
1
module Sass
-
1
module Script
-
# The parser for SassScript.
-
# It parses a string of code into a tree of {Script::Node}s.
-
1
class Parser
-
# The line number of the parser's current position.
-
#
-
# @return [Fixnum]
-
1
def line
-
@lexer.line
-
end
-
-
# @param str [String, StringScanner] The source text to parse
-
# @param line [Fixnum] The line on which the SassScript appears.
-
# Used for error reporting
-
# @param offset [Fixnum] The number of characters in on which the SassScript appears.
-
# Used for error reporting
-
# @param options [{Symbol => Object}] An options hash;
-
# see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
-
1
def initialize(str, line, offset, options = {})
-
@options = options
-
@lexer = lexer_class.new(str, line, offset, options)
-
end
-
-
# Parses a SassScript expression within an interpolated segment (`#{}`).
-
# This means that it stops when it comes across an unmatched `}`,
-
# which signals the end of an interpolated segment,
-
# it returns rather than throwing an error.
-
#
-
# @return [Script::Node] The root node of the parse tree
-
# @raise [Sass::SyntaxError] if the expression isn't valid SassScript
-
1
def parse_interpolated
-
expr = assert_expr :expr
-
assert_tok :end_interpolation
-
expr.options = @options
-
expr
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
-
raise e
-
end
-
-
# Parses a SassScript expression.
-
#
-
# @return [Script::Node] The root node of the parse tree
-
# @raise [Sass::SyntaxError] if the expression isn't valid SassScript
-
1
def parse
-
expr = assert_expr :expr
-
assert_done
-
expr.options = @options
-
expr
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
-
raise e
-
end
-
-
# Parses a SassScript expression,
-
# ending it when it encounters one of the given identifier tokens.
-
#
-
# @param [#include?(String)] A set of strings that delimit the expression.
-
# @return [Script::Node] The root node of the parse tree
-
# @raise [Sass::SyntaxError] if the expression isn't valid SassScript
-
1
def parse_until(tokens)
-
@stop_at = tokens
-
expr = assert_expr :expr
-
assert_done
-
expr.options = @options
-
expr
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
-
raise e
-
end
-
-
# Parses the argument list for a mixin include.
-
#
-
# @return [(Array<Script::Node>, {String => Script::Note})]
-
# The root nodes of the arguments.
-
# Keyword arguments are in a hash from names to values.
-
# @raise [Sass::SyntaxError] if the argument list isn't valid SassScript
-
1
def parse_mixin_include_arglist
-
args, keywords = [], {}
-
if try_tok(:lparen)
-
args, keywords = mixin_arglist || [[], {}]
-
assert_tok(:rparen)
-
end
-
assert_done
-
-
args.each {|a| a.options = @options}
-
keywords.each {|k, v| v.options = @options}
-
return args, keywords
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
-
raise e
-
end
-
-
# Parses the argument list for a mixin definition.
-
#
-
# @return [Array<Script::Node>] The root nodes of the arguments.
-
# @raise [Sass::SyntaxError] if the argument list isn't valid SassScript
-
1
def parse_mixin_definition_arglist
-
args = defn_arglist!(false)
-
assert_done
-
-
args.each do |k, v|
-
k.options = @options
-
v.options = @options if v
-
end
-
args
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
-
raise e
-
end
-
-
# Parses the argument list for a function definition.
-
#
-
# @return [Array<Script::Node>] The root nodes of the arguments.
-
# @raise [Sass::SyntaxError] if the argument list isn't valid SassScript
-
1
def parse_function_definition_arglist
-
args = defn_arglist!(true)
-
assert_done
-
-
args.each do |k, v|
-
k.options = @options
-
v.options = @options if v
-
end
-
args
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
-
raise e
-
end
-
-
# Parses a SassScript expression.
-
#
-
# @overload parse(str, line, offset, filename = nil)
-
# @return [Script::Node] The root node of the parse tree
-
# @see Parser#initialize
-
# @see Parser#parse
-
1
def self.parse(*args)
-
new(*args).parse
-
end
-
-
1
PRECEDENCE = [
-
:comma, :single_eq, :space, :or, :and,
-
[:eq, :neq],
-
[:gt, :gte, :lt, :lte],
-
[:plus, :minus],
-
[:times, :div, :mod],
-
]
-
-
1
ASSOCIATIVE = [:plus, :times]
-
-
1
class << self
-
# Returns an integer representing the precedence
-
# of the given operator.
-
# A lower integer indicates a looser binding.
-
#
-
# @private
-
1
def precedence_of(op)
-
PRECEDENCE.each_with_index do |e, i|
-
return i if Array(e).include?(op)
-
end
-
raise "[BUG] Unknown operator #{op}"
-
end
-
-
# Returns whether or not the given operation is associative.
-
#
-
# @private
-
1
def associative?(op)
-
ASSOCIATIVE.include?(op)
-
end
-
-
1
private
-
-
# Defines a simple left-associative production.
-
# name is the name of the production,
-
# sub is the name of the production beneath it,
-
# and ops is a list of operators for this precedence level
-
1
def production(name, sub, *ops)
-
class_eval <<RUBY
-
def #{name}
-
interp = try_ops_after_interp(#{ops.inspect}, #{name.inspect}) and return interp
-
return unless e = #{sub}
-
15
while tok = try_tok(#{ops.map {|o| o.inspect}.join(', ')})
-
if interp = try_op_before_interp(tok, e)
-
return interp unless other_interp = try_ops_after_interp(#{ops.inspect}, #{name.inspect}, interp)
-
return other_interp
-
end
-
-
line = @lexer.line
-
e = Operation.new(e, assert_expr(#{sub.inspect}), tok.type)
-
e.line = line
-
end
-
e
-
end
-
8
RUBY
-
end
-
-
1
def unary(op, sub)
-
class_eval <<RUBY
-
def unary_#{op}
-
return #{sub} unless tok = try_tok(:#{op})
-
interp = try_op_before_interp(tok) and return interp
-
line = @lexer.line
-
op = UnaryOperation.new(assert_expr(:unary_#{op}), :#{op})
-
op.line = line
-
op
-
end
-
4
RUBY
-
end
-
end
-
-
1
private
-
-
# @private
-
1
def lexer_class; Lexer; end
-
-
1
def expr
-
interp = try_ops_after_interp([:comma], :expr) and return interp
-
line = @lexer.line
-
return unless e = interpolation
-
arr = [e]
-
while tok = try_tok(:comma)
-
if interp = try_op_before_interp(tok, e)
-
return interp unless other_interp = try_ops_after_interp([:comma], :expr, interp)
-
return other_interp
-
end
-
arr << assert_expr(:interpolation)
-
end
-
arr.size == 1 ? arr.first : node(List.new(arr, :comma), line)
-
end
-
-
1
production :equals, :interpolation, :single_eq
-
-
1
def try_op_before_interp(op, prev = nil)
-
return unless @lexer.peek && @lexer.peek.type == :begin_interpolation
-
wb = @lexer.whitespace?(op)
-
str = Script::String.new(Lexer::OPERATORS_REVERSE[op.type])
-
str.line = @lexer.line
-
interp = Script::Interpolation.new(prev, str, nil, wb, !:wa, :originally_text)
-
interp.line = @lexer.line
-
interpolation(interp)
-
end
-
-
1
def try_ops_after_interp(ops, name, prev = nil)
-
return unless @lexer.after_interpolation?
-
return unless op = try_tok(*ops)
-
interp = try_op_before_interp(op, prev) and return interp
-
-
wa = @lexer.whitespace?
-
str = Script::String.new(Lexer::OPERATORS_REVERSE[op.type])
-
str.line = @lexer.line
-
interp = Script::Interpolation.new(prev, str, assert_expr(name), !:wb, wa, :originally_text)
-
interp.line = @lexer.line
-
return interp
-
end
-
-
1
def interpolation(first = space)
-
e = first
-
while interp = try_tok(:begin_interpolation)
-
wb = @lexer.whitespace?(interp)
-
line = @lexer.line
-
mid = parse_interpolated
-
wa = @lexer.whitespace?
-
e = Script::Interpolation.new(e, mid, space, wb, wa)
-
e.line = line
-
end
-
e
-
end
-
-
1
def space
-
line = @lexer.line
-
return unless e = or_expr
-
arr = [e]
-
while e = or_expr
-
arr << e
-
end
-
arr.size == 1 ? arr.first : node(List.new(arr, :space), line)
-
end
-
-
1
production :or_expr, :and_expr, :or
-
1
production :and_expr, :eq_or_neq, :and
-
1
production :eq_or_neq, :relational, :eq, :neq
-
1
production :relational, :plus_or_minus, :gt, :gte, :lt, :lte
-
1
production :plus_or_minus, :times_div_or_mod, :plus, :minus
-
1
production :times_div_or_mod, :unary_plus, :times, :div, :mod
-
-
1
unary :plus, :unary_minus
-
1
unary :minus, :unary_div
-
1
unary :div, :unary_not # For strings, so /foo/bar works
-
1
unary :not, :ident
-
-
1
def ident
-
return funcall unless @lexer.peek && @lexer.peek.type == :ident
-
return if @stop_at && @stop_at.include?(@lexer.peek.value)
-
-
name = @lexer.next
-
if color = Color::HTML4_COLORS[name.value.downcase]
-
return node(Color.new(color))
-
end
-
node(Script::String.new(name.value, :identifier))
-
end
-
-
1
def funcall
-
return raw unless tok = try_tok(:funcall)
-
args, keywords = fn_arglist || [[], {}]
-
assert_tok(:rparen)
-
node(Script::Funcall.new(tok.value, args, keywords))
-
end
-
-
1
def defn_arglist!(must_have_parens)
-
if must_have_parens
-
assert_tok(:lparen)
-
else
-
return [] unless try_tok(:lparen)
-
end
-
return [] if try_tok(:rparen)
-
-
res = []
-
must_have_default = false
-
loop do
-
line = @lexer.line
-
offset = @lexer.offset + 1
-
c = assert_tok(:const)
-
var = Script::Variable.new(c.value)
-
if tok = try_tok(:colon)
-
val = assert_expr(:space)
-
must_have_default = true
-
elsif must_have_default
-
raise SyntaxError.new("Required argument #{var.inspect} must come before any optional arguments.")
-
end
-
res << [var, val]
-
break unless try_tok(:comma)
-
end
-
assert_tok(:rparen)
-
res
-
end
-
-
1
def fn_arglist
-
arglist(:fn_arglist, :equals)
-
end
-
-
1
def mixin_arglist
-
arglist(:mixin_arglist, :interpolation)
-
end
-
-
1
def arglist(type, subexpr)
-
return unless e = send(subexpr)
-
if @lexer.peek && @lexer.peek.type == :colon
-
name = e
-
@lexer.expected!("comma") unless name.is_a?(Variable)
-
assert_tok(:colon)
-
keywords = {name.underscored_name => assert_expr(subexpr, EXPR_NAMES[type])}
-
end
-
-
unless try_tok(:comma)
-
return [], keywords if keywords
-
return [e], {}
-
end
-
-
other_args, other_keywords = assert_expr(type)
-
if keywords
-
if other_keywords[name.underscored_name]
-
raise SyntaxError.new("Keyword argument \"#{name.to_sass}\" passed more than once")
-
end
-
return other_args, keywords.merge(other_keywords)
-
else
-
return [e, *other_args], other_keywords
-
end
-
end
-
-
1
def keyword_arglist
-
return unless var = try_tok(:const)
-
unless try_tok(:colon)
-
return_tok!
-
return
-
end
-
name = var[1]
-
value = interpolation
-
return {name => value} unless try_tok(:comma)
-
{name => value}.merge(assert_expr(:keyword_arglist))
-
end
-
-
1
def raw
-
return special_fun unless tok = try_tok(:raw)
-
node(Script::String.new(tok.value))
-
end
-
-
1
def special_fun
-
return paren unless tok = try_tok(:special_fun)
-
first = node(Script::String.new(tok.value.first))
-
Sass::Util.enum_slice(tok.value[1..-1], 2).inject(first) do |l, (i, r)|
-
Script::Interpolation.new(
-
l, i, r && node(Script::String.new(r)),
-
false, false)
-
end
-
end
-
-
1
def paren
-
return variable unless try_tok(:lparen)
-
was_in_parens = @in_parens
-
@in_parens = true
-
line = @lexer.line
-
e = expr
-
assert_tok(:rparen)
-
return e || node(List.new([], :space), line)
-
ensure
-
@in_parens = was_in_parens
-
end
-
-
1
def variable
-
return string unless c = try_tok(:const)
-
node(Variable.new(*c.value))
-
end
-
-
1
def string
-
return number unless first = try_tok(:string)
-
return first.value unless try_tok(:begin_interpolation)
-
line = @lexer.line
-
mid = parse_interpolated
-
last = assert_expr(:string)
-
interp = StringInterpolation.new(first.value, mid, last)
-
interp.line = line
-
interp
-
end
-
-
1
def number
-
return literal unless tok = try_tok(:number)
-
num = tok.value
-
num.original = num.to_s unless @in_parens
-
num
-
end
-
-
1
def literal
-
(t = try_tok(:color, :bool)) && (return t.value)
-
end
-
-
# It would be possible to have unified #assert and #try methods,
-
# but detecting the method/token difference turns out to be quite expensive.
-
-
1
EXPR_NAMES = {
-
:string => "string",
-
:default => "expression (e.g. 1px, bold)",
-
:mixin_arglist => "mixin argument",
-
:fn_arglist => "function argument",
-
}
-
-
1
def assert_expr(name, expected = nil)
-
(e = send(name)) && (return e)
-
@lexer.expected!(expected || EXPR_NAMES[name] || EXPR_NAMES[:default])
-
end
-
-
1
def assert_tok(*names)
-
(t = try_tok(*names)) && (return t)
-
@lexer.expected!(names.map {|tok| Lexer::TOKEN_NAMES[tok] || tok}.join(" or "))
-
end
-
-
1
def try_tok(*names)
-
peeked = @lexer.peek
-
peeked && names.include?(peeked.type) && @lexer.next
-
end
-
-
1
def assert_done
-
return if @lexer.done?
-
@lexer.expected!(EXPR_NAMES[:default])
-
end
-
-
1
def node(node, line = @lexer.line)
-
node.line = line
-
node
-
end
-
end
-
end
-
end
-
1
require 'sass/script/literal'
-
-
1
module Sass::Script
-
# A SassScript object representing a CSS string *or* a CSS identifier.
-
1
class String < Literal
-
# The Ruby value of the string.
-
#
-
# @return [String]
-
1
attr_reader :value
-
-
# Whether this is a CSS string or a CSS identifier.
-
# The difference is that strings are written with double-quotes,
-
# while identifiers aren't.
-
#
-
# @return [Symbol] `:string` or `:identifier`
-
1
attr_reader :type
-
-
# Creates a new string.
-
#
-
# @param value [String] See \{#value}
-
# @param type [Symbol] See \{#type}
-
1
def initialize(value, type = :identifier)
-
super(value)
-
@type = type
-
end
-
-
# @see Literal#plus
-
1
def plus(other)
-
other_str = other.is_a?(Sass::Script::String) ? other.value : other.to_s
-
Sass::Script::String.new(self.value + other_str, self.type)
-
end
-
-
# @see Node#to_s
-
1
def to_s(opts = {})
-
if @type == :identifier
-
return @value.tr("\n", " ")
-
end
-
-
return "\"#{value.gsub('"', "\\\"")}\"" if opts[:quote] == %q{"}
-
return "'#{value.gsub("'", "\\'")}'" if opts[:quote] == %q{'}
-
return "\"#{value}\"" unless value.include?('"')
-
return "'#{value}'" unless value.include?("'")
-
"\"#{value.gsub('"', "\\\"")}\"" #'
-
end
-
-
# @see Node#to_sass
-
1
def to_sass(opts = {})
-
to_s
-
end
-
end
-
end
-
1
module Sass::Script
-
# A SassScript object representing `#{}` interpolation within a string.
-
#
-
# @see Interpolation
-
1
class StringInterpolation < Node
-
# Interpolation in a string is of the form `"before #{mid} after"`,
-
# where `before` and `after` may include more interpolation.
-
#
-
# @param before [Node] The string before the interpolation
-
# @param mid [Node] The SassScript within the interpolation
-
# @param after [Node] The string after the interpolation
-
1
def initialize(before, mid, after)
-
@before = before
-
@mid = mid
-
@after = after
-
end
-
-
# @return [String] A human-readable s-expression representation of the interpolation
-
1
def inspect
-
"(string_interpolation #{@before.inspect} #{@mid.inspect} #{@after.inspect})"
-
end
-
-
# @see Node#to_sass
-
1
def to_sass(opts = {})
-
# We can get rid of all of this when we remove the deprecated :equals context
-
# XXX CE: It's gone now but I'm not sure what can be removed now.
-
before_unquote, before_quote_char, before_str = parse_str(@before.to_sass(opts))
-
after_unquote, after_quote_char, after_str = parse_str(@after.to_sass(opts))
-
unquote = before_unquote || after_unquote ||
-
(before_quote_char && !after_quote_char && !after_str.empty?) ||
-
(!before_quote_char && after_quote_char && !before_str.empty?)
-
quote_char =
-
if before_quote_char && after_quote_char && before_quote_char != after_quote_char
-
before_str.gsub!("\\'", "'")
-
before_str.gsub!('"', "\\\"")
-
after_str.gsub!("\\'", "'")
-
after_str.gsub!('"', "\\\"")
-
'"'
-
else
-
before_quote_char || after_quote_char
-
end
-
-
res = ""
-
res << 'unquote(' if unquote
-
res << quote_char if quote_char
-
res << before_str
-
res << '#{' << @mid.to_sass(opts) << '}'
-
res << after_str
-
res << quote_char if quote_char
-
res << ')' if unquote
-
res
-
end
-
-
# Returns the three components of the interpolation, `before`, `mid`, and `after`.
-
#
-
# @return [Array<Node>]
-
# @see #initialize
-
# @see Node#children
-
1
def children
-
[@before, @mid, @after].compact
-
end
-
-
# @see Node#deep_copy
-
1
def deep_copy
-
node = dup
-
node.instance_variable_set('@before', @before.deep_copy) if @before
-
node.instance_variable_set('@mid', @mid.deep_copy)
-
node.instance_variable_set('@after', @after.deep_copy) if @after
-
node
-
end
-
-
1
protected
-
-
# Evaluates the interpolation.
-
#
-
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
-
# @return [Sass::Script::String] The SassScript string that is the value of the interpolation
-
1
def _perform(environment)
-
res = ""
-
before = @before.perform(environment)
-
res << before.value
-
mid = @mid.perform(environment)
-
res << (mid.is_a?(Sass::Script::String) ? mid.value : mid.to_s)
-
res << @after.perform(environment).value
-
opts(Sass::Script::String.new(res, before.type))
-
end
-
-
1
private
-
-
1
def parse_str(str)
-
case str
-
when /^unquote\((["'])(.*)\1\)$/
-
return true, $1, $2
-
when '""'
-
return false, nil, ""
-
when /^(["'])(.*)\1$/
-
return false, $1, $2
-
else
-
return false, nil, str
-
end
-
end
-
end
-
end
-
1
module Sass::Script
-
# A SassScript parse node representing a unary operation,
-
# such as `-$b` or `not true`.
-
#
-
# Currently only `-`, `/`, and `not` are unary operators.
-
1
class UnaryOperation < Node
-
# @param operand [Script::Node] The parse-tree node
-
# for the object of the operator
-
# @param operator [Symbol] The operator to perform
-
1
def initialize(operand, operator)
-
@operand = operand
-
@operator = operator
-
super()
-
end
-
-
# @return [String] A human-readable s-expression representation of the operation
-
1
def inspect
-
"(#{@operator.inspect} #{@operand.inspect})"
-
end
-
-
# @see Node#to_sass
-
1
def to_sass(opts = {})
-
operand = @operand.to_sass(opts)
-
if @operand.is_a?(Operation) ||
-
(@operator == :minus &&
-
(operand =~ Sass::SCSS::RX::IDENT) == 0)
-
operand = "(#{@operand.to_sass(opts)})"
-
end
-
op = Lexer::OPERATORS_REVERSE[@operator]
-
op + (op =~ /[a-z]/ ? " " : "") + operand
-
end
-
-
# Returns the operand of the operation.
-
#
-
# @return [Array<Node>]
-
# @see Node#children
-
1
def children
-
[@operand]
-
end
-
-
# @see Node#deep_copy
-
1
def deep_copy
-
node = dup
-
node.instance_variable_set('@operand', @operand.deep_copy)
-
node
-
end
-
-
1
protected
-
-
# Evaluates the operation.
-
#
-
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
-
# @return [Literal] The SassScript object that is the value of the operation
-
# @raise [Sass::SyntaxError] if the operation is undefined for the operand
-
1
def _perform(environment)
-
operator = "unary_#{@operator}"
-
literal = @operand.perform(environment)
-
literal.send(operator)
-
rescue NoMethodError => e
-
raise e unless e.name.to_s == operator.to_s
-
raise Sass::SyntaxError.new("Undefined unary operation: \"#{@operator} #{literal}\".")
-
end
-
end
-
end
-
1
module Sass
-
1
module Script
-
# A SassScript parse node representing a variable.
-
1
class Variable < Node
-
# The name of the variable.
-
#
-
# @return [String]
-
1
attr_reader :name
-
-
# The underscored name of the variable.
-
#
-
# @return [String]
-
1
attr_reader :underscored_name
-
-
# @param name [String] See \{#name}
-
1
def initialize(name)
-
@name = name
-
@underscored_name = name.gsub(/-/,"_")
-
super()
-
end
-
-
# @return [String] A string representation of the variable
-
1
def inspect(opts = {})
-
"$#{dasherize(name, opts)}"
-
end
-
1
alias_method :to_sass, :inspect
-
-
# Returns an empty array.
-
#
-
# @return [Array<Node>] empty
-
# @see Node#children
-
1
def children
-
[]
-
end
-
-
# @see Node#deep_copy
-
1
def deep_copy
-
dup
-
end
-
-
1
protected
-
-
# Evaluates the variable.
-
#
-
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
-
# @return [Literal] The SassScript object that is the value of the variable
-
# @raise [Sass::SyntaxError] if the variable is undefined
-
1
def _perform(environment)
-
raise SyntaxError.new("Undefined variable: \"$#{name}\".") unless val = environment.var(name)
-
if val.is_a?(Number)
-
val = val.dup
-
val.original = nil
-
end
-
return val
-
end
-
end
-
end
-
end
-
1
require 'sass/scss/rx'
-
1
require 'sass/scss/script_lexer'
-
1
require 'sass/scss/script_parser'
-
1
require 'sass/scss/parser'
-
1
require 'sass/scss/sass_parser'
-
1
require 'sass/scss/static_parser'
-
1
require 'sass/scss/css_parser'
-
-
1
module Sass
-
# SCSS is the CSS syntax for Sass.
-
# It parses into the same syntax tree as Sass,
-
# and generates the same sort of output CSS.
-
#
-
# This module contains code for the parsing of SCSS.
-
# The evaluation is handled by the broader {Sass} module.
-
1
module SCSS; end
-
end
-
1
require 'sass/script/css_parser'
-
-
1
module Sass
-
1
module SCSS
-
# This is a subclass of {Parser} which only parses plain CSS.
-
# It doesn't support any Sass extensions, such as interpolation,
-
# parent references, nested selectors, and so forth.
-
# It does support all the same CSS hacks as the SCSS parser, though.
-
1
class CssParser < StaticParser
-
# Parse a selector, and return its value as a string.
-
#
-
# @return [String, nil] The parsed selector, or nil if no selector was parsed
-
# @raise [Sass::SyntaxError] if there's a syntax error in the selector
-
1
def parse_selector_string
-
init_scanner!
-
str {return unless selector}
-
end
-
-
1
private
-
-
1
def parent_selector; nil; end
-
1
def interpolation; nil; end
-
1
def interp_string; tok(STRING); end
-
1
def interp_ident(ident = IDENT); tok(ident); end
-
1
def use_css_import?; true; end
-
-
1
def block_child(context)
-
case context
-
when :ruleset
-
declaration
-
when :stylesheet
-
directive || ruleset
-
when :directive
-
directive || declaration_or_ruleset
-
end
-
end
-
-
1
def nested_properties!(node, space)
-
expected('expression (e.g. 1px, bold)');
-
end
-
-
1
@sass_script_parser = Class.new(Sass::Script::CssParser)
-
1
@sass_script_parser.send(:include, ScriptParser)
-
end
-
end
-
end
-
1
require 'set'
-
-
1
module Sass
-
1
module SCSS
-
# The parser for SCSS.
-
# It parses a string of code into a tree of {Sass::Tree::Node}s.
-
1
class Parser
-
# @param str [String, StringScanner] The source document to parse.
-
# Note that `Parser` *won't* raise a nice error message if this isn't properly parsed;
-
# for that, you should use the higher-level {Sass::Engine} or {Sass::CSS}.
-
# @param filename [String] The name of the file being parsed. Used for warnings.
-
# @param line [Fixnum] The line on which the source string appeared,
-
# if it's part of another document.
-
1
def initialize(str, filename, line = 1)
-
@template = str
-
@filename = filename
-
@line = line
-
@strs = []
-
end
-
-
# Parses an SCSS document.
-
#
-
# @return [Sass::Tree::RootNode] The root node of the document tree
-
# @raise [Sass::SyntaxError] if there's a syntax error in the document
-
1
def parse
-
init_scanner!
-
root = stylesheet
-
expected("selector or at-rule") unless @scanner.eos?
-
root
-
end
-
-
# Parses an identifier with interpolation.
-
# Note that this won't assert that the identifier takes up the entire input string;
-
# it's meant to be used with `StringScanner`s as part of other parsers.
-
#
-
# @return [Array<String, Sass::Script::Node>, nil]
-
# The interpolated identifier, or nil if none could be parsed
-
1
def parse_interp_ident
-
init_scanner!
-
interp_ident
-
end
-
-
1
private
-
-
1
include Sass::SCSS::RX
-
-
1
def init_scanner!
-
@scanner =
-
if @template.is_a?(StringScanner)
-
@template
-
else
-
Sass::Util::MultibyteStringScanner.new(@template.gsub("\r", ""))
-
end
-
end
-
-
1
def stylesheet
-
node = node(Sass::Tree::RootNode.new(@scanner.string))
-
block_contents(node, :stylesheet) {s(node)}
-
end
-
-
1
def s(node)
-
while tok(S) || tok(CDC) || tok(CDO) || (c = tok(SINGLE_LINE_COMMENT)) || (c = tok(COMMENT))
-
next unless c
-
process_comment c, node
-
c = nil
-
end
-
true
-
end
-
-
1
def ss
-
nil while tok(S) || tok(SINGLE_LINE_COMMENT) || tok(COMMENT)
-
true
-
end
-
-
1
def ss_comments(node)
-
while tok(S) || (c = tok(SINGLE_LINE_COMMENT)) || (c = tok(COMMENT))
-
next unless c
-
process_comment c, node
-
c = nil
-
end
-
-
true
-
end
-
-
1
def whitespace
-
return unless tok(S) || tok(SINGLE_LINE_COMMENT) || tok(COMMENT)
-
ss
-
end
-
-
1
def process_comment(text, node)
-
silent = text =~ /^\/\//
-
line = @line - text.count("\n")
-
if loud = text =~ %r{^/[/*]!}
-
value = Sass::Engine.parse_interp(text, line, @scanner.pos - text.size, :filename => @filename)
-
value[0].slice!(2) # get rid of the "!"
-
else
-
value = [text]
-
end
-
-
if silent
-
value = Sass::Util.with_extracted_values(value) do |str|
-
str.sub(/^\s*\/\//, '/*').gsub(/^\s*\/\//, ' *') + ' */'
-
end
-
else
-
value.unshift(@scanner.
-
string[0...@scanner.pos].
-
reverse[/.*?\*\/(.*?)($|\Z)/, 1].
-
reverse.gsub(/[^\s]/, ' '))
-
end
-
-
comment = Sass::Tree::CommentNode.new(value, silent, loud)
-
comment.line = line
-
node << comment
-
end
-
-
1
DIRECTIVES = Set[:mixin, :include, :function, :return, :debug, :warn, :for,
-
:each, :while, :if, :else, :extend, :import, :media, :charset, :_moz_document]
-
-
1
PREFIXED_DIRECTIVES = Set[:supports]
-
-
1
def directive
-
return unless tok(/@/)
-
name = tok!(IDENT)
-
ss
-
-
if dir = special_directive(name)
-
return dir
-
elsif dir = prefixed_directive(name)
-
return dir
-
end
-
-
# Most at-rules take expressions (e.g. @import),
-
# but some (e.g. @page) take selector-like arguments
-
val = str {break unless expr}
-
val ||= CssParser.new(@scanner, @line).parse_selector_string
-
directive_body("@#{name} #{val}")
-
end
-
-
1
def directive_body(value)
-
node = node(Sass::Tree::DirectiveNode.new(value.strip))
-
-
if tok(/\{/)
-
node.has_children = true
-
block_contents(node, :directive)
-
tok!(/\}/)
-
end
-
-
node
-
end
-
-
1
def special_directive(name)
-
sym = name.gsub('-', '_').to_sym
-
DIRECTIVES.include?(sym) && send("#{sym}_directive")
-
end
-
-
1
def prefixed_directive(name)
-
sym = name.gsub(/^-[a-z0-9]+-/i, '').gsub('-', '_').to_sym
-
PREFIXED_DIRECTIVES.include?(sym) && send("#{sym}_directive", name)
-
end
-
-
1
def mixin_directive
-
name = tok! IDENT
-
args = sass_script(:parse_mixin_definition_arglist)
-
ss
-
block(node(Sass::Tree::MixinDefNode.new(name, args)), :directive)
-
end
-
-
1
def include_directive
-
name = tok! IDENT
-
args, keywords = sass_script(:parse_mixin_include_arglist)
-
ss
-
node(Sass::Tree::MixinNode.new(name, args, keywords))
-
end
-
-
1
def function_directive
-
name = tok! IDENT
-
args = sass_script(:parse_function_definition_arglist)
-
ss
-
block(node(Sass::Tree::FunctionNode.new(name, args)), :function)
-
end
-
-
1
def return_directive
-
node(Sass::Tree::ReturnNode.new(sass_script(:parse)))
-
end
-
-
1
def debug_directive
-
node(Sass::Tree::DebugNode.new(sass_script(:parse)))
-
end
-
-
1
def warn_directive
-
node(Sass::Tree::WarnNode.new(sass_script(:parse)))
-
end
-
-
1
def for_directive
-
tok!(/\$/)
-
var = tok! IDENT
-
ss
-
-
tok!(/from/)
-
from = sass_script(:parse_until, Set["to", "through"])
-
ss
-
-
@expected = '"to" or "through"'
-
exclusive = (tok(/to/) || tok!(/through/)) == 'to'
-
to = sass_script(:parse)
-
ss
-
-
block(node(Sass::Tree::ForNode.new(var, from, to, exclusive)), :directive)
-
end
-
-
1
def each_directive
-
tok!(/\$/)
-
var = tok! IDENT
-
ss
-
-
tok!(/in/)
-
list = sass_script(:parse)
-
ss
-
-
block(node(Sass::Tree::EachNode.new(var, list)), :directive)
-
end
-
-
1
def while_directive
-
expr = sass_script(:parse)
-
ss
-
block(node(Sass::Tree::WhileNode.new(expr)), :directive)
-
end
-
-
1
def if_directive
-
expr = sass_script(:parse)
-
ss
-
node = block(node(Sass::Tree::IfNode.new(expr)), :directive)
-
pos = @scanner.pos
-
line = @line
-
ss
-
-
else_block(node) ||
-
begin
-
# Backtrack in case there are any comments we want to parse
-
@scanner.pos = pos
-
@line = line
-
node
-
end
-
end
-
-
1
def else_block(node)
-
return unless tok(/@else/)
-
ss
-
else_node = block(
-
Sass::Tree::IfNode.new((sass_script(:parse) if tok(/if/))),
-
:directive)
-
node.add_else(else_node)
-
pos = @scanner.pos
-
line = @line
-
ss
-
-
else_block(node) ||
-
begin
-
# Backtrack in case there are any comments we want to parse
-
@scanner.pos = pos
-
@line = line
-
node
-
end
-
end
-
-
1
def else_directive
-
err("Invalid CSS: @else must come after @if")
-
end
-
-
1
def extend_directive
-
node(Sass::Tree::ExtendNode.new(expr!(:selector_sequence)))
-
end
-
-
1
def import_directive
-
values = []
-
-
loop do
-
values << expr!(:import_arg)
-
break if use_css_import? || !tok(/,\s*/)
-
end
-
-
return values
-
end
-
-
1
def import_arg
-
return unless arg = tok(STRING) || (uri = tok!(URI))
-
path = @scanner[1] || @scanner[2] || @scanner[3]
-
ss
-
-
media = str {media_query_list}.strip
-
-
if uri || path =~ /^http:\/\// || !media.strip.empty? || use_css_import?
-
return node(Sass::Tree::DirectiveNode.new("@import #{arg} #{media}".strip))
-
end
-
-
node(Sass::Tree::ImportNode.new(path.strip))
-
end
-
-
1
def use_css_import?; false; end
-
-
1
def media_directive
-
block(node(Sass::Tree::MediaNode.new(media_query_list)), :directive)
-
end
-
-
# http://www.w3.org/TR/css3-mediaqueries/#syntax
-
1
def media_query_list
-
has_q = false
-
q = str {has_q = media_query}
-
-
return unless has_q
-
queries = [q.strip]
-
-
ss
-
while tok(/,/)
-
ss; queries << str {expr!(:media_query)}.strip; ss
-
end
-
-
queries
-
end
-
-
1
def media_query
-
if tok(/only|not/i)
-
ss
-
@expected = "media type (e.g. print, screen)"
-
tok!(IDENT)
-
ss
-
elsif !tok(IDENT) && !media_expr
-
return
-
end
-
-
ss
-
while tok(/and/i)
-
ss; expr!(:media_expr); ss
-
end
-
-
true
-
end
-
-
1
def media_expr
-
return unless tok(/\(/)
-
ss
-
@expected = "media feature (e.g. min-device-width, color)"
-
tok!(IDENT)
-
ss
-
-
if tok(/:/)
-
ss; expr!(:expr)
-
end
-
tok!(/\)/)
-
ss
-
-
true
-
end
-
-
1
def charset_directive
-
tok! STRING
-
name = @scanner[1] || @scanner[2]
-
ss
-
node(Sass::Tree::CharsetNode.new(name))
-
end
-
-
# The document directive is specified in
-
# http://www.w3.org/TR/css3-conditional/, but Gecko allows the
-
# `url-prefix` and `domain` functions to omit quotation marks, contrary to
-
# the standard.
-
#
-
# We could parse all document directives according to Mozilla's syntax,
-
# but if someone's using e.g. @-webkit-document we don't want them to
-
# think WebKit works sans quotes.
-
1
def _moz_document_directive
-
value = str do
-
begin
-
ss
-
expr!(:moz_document_function)
-
end while tok(/,/)
-
end
-
directive_body("@-moz-document #{value}")
-
end
-
-
1
def moz_document_function
-
return unless tok(URI) || tok(URL_PREFIX) || tok(DOMAIN) || function
-
ss
-
end
-
-
# http://www.w3.org/TR/css3-conditional/
-
1
def supports_directive(name)
-
value = str {expr!(:supports_condition)}
-
directive_body("@#{name} #{value}")
-
end
-
-
1
def supports_condition
-
supports_negation || supports_operator || supports_declaration_condition
-
end
-
-
1
def supports_negation
-
return unless tok(/not/i)
-
ss
-
expr!(:supports_condition_in_parens)
-
end
-
-
1
def supports_operator
-
return unless supports_condition_in_parens
-
tok!(/and|or/i)
-
begin
-
ss
-
expr!(:supports_condition_in_parens)
-
end while tok(/and|or/i)
-
true
-
end
-
-
1
def supports_condition_in_parens
-
return unless tok(/\(/); ss
-
if supports_condition
-
tok!(/\)/); ss
-
else
-
supports_declaration_body
-
end
-
end
-
-
1
def supports_declaration_condition
-
return unless tok(/\(/); ss
-
supports_declaration_body
-
end
-
-
1
def supports_declaration_body
-
tok!(IDENT); ss
-
tok!(/:/); ss
-
expr!(:expr); ss
-
tok!(/\)/); ss
-
end
-
-
1
def variable
-
return unless tok(/\$/)
-
name = tok!(IDENT)
-
ss; tok!(/:/); ss
-
-
expr = sass_script(:parse)
-
guarded = tok(DEFAULT)
-
node(Sass::Tree::VariableNode.new(name, expr, guarded))
-
end
-
-
1
def operator
-
# Many of these operators (all except / and ,)
-
# are disallowed by the CSS spec,
-
# but they're included here for compatibility
-
# with some proprietary MS properties
-
str {ss if tok(/[\/,:.=]/)}
-
end
-
-
1
def unary_operator
-
tok(/[+-]/)
-
end
-
-
1
def ruleset
-
return unless rules = selector_sequence
-
block(node(Sass::Tree::RuleNode.new(rules.flatten.compact)), :ruleset)
-
end
-
-
1
def block(node, context)
-
node.has_children = true
-
tok!(/\{/)
-
block_contents(node, context)
-
tok!(/\}/)
-
node
-
end
-
-
# A block may contain declarations and/or rulesets
-
1
def block_contents(node, context)
-
block_given? ? yield : ss_comments(node)
-
node << (child = block_child(context))
-
while tok(/;/) || has_children?(child)
-
block_given? ? yield : ss_comments(node)
-
node << (child = block_child(context))
-
end
-
node
-
end
-
-
1
def block_child(context)
-
return variable || directive if context == :function
-
return variable || directive || ruleset if context == :stylesheet
-
variable || directive || declaration_or_ruleset
-
end
-
-
1
def has_children?(child_or_array)
-
return false unless child_or_array
-
return child_or_array.last.has_children if child_or_array.is_a?(Array)
-
return child_or_array.has_children
-
end
-
-
# This is a nasty hack, and the only place in the parser
-
# that requires backtracking.
-
# The reason is that we can't figure out if certain strings
-
# are declarations or rulesets with fixed finite lookahead.
-
# For example, "foo:bar baz baz baz..." could be either a property
-
# or a selector.
-
#
-
# To handle this, we simply check if it works as a property
-
# (which is the most common case)
-
# and, if it doesn't, try it as a ruleset.
-
#
-
# We could eke some more efficiency out of this
-
# by handling some easy cases (first token isn't an identifier,
-
# no colon after the identifier, whitespace after the colon),
-
# but I'm not sure the gains would be worth the added complexity.
-
1
def declaration_or_ruleset
-
old_use_property_exception, @use_property_exception =
-
@use_property_exception, false
-
decl_err = catch_error do
-
decl = declaration
-
unless decl && decl.has_children
-
# We want an exception if it's not there,
-
# but we don't want to consume if it is
-
tok!(/[;}]/) unless tok?(/[;}]/)
-
end
-
return decl
-
end
-
-
ruleset_err = catch_error {return ruleset}
-
rethrow(@use_property_exception ? decl_err : ruleset_err)
-
ensure
-
@use_property_exception = old_use_property_exception
-
end
-
-
1
def selector_sequence
-
if sel = tok(STATIC_SELECTOR, true)
-
return [sel]
-
end
-
-
rules = []
-
return unless v = selector
-
rules.concat v
-
-
ws = ''
-
while tok(/,/)
-
ws << str {ss}
-
if v = selector
-
rules << ',' << ws
-
rules.concat v
-
ws = ''
-
end
-
end
-
rules
-
end
-
-
1
def selector
-
return unless sel = _selector
-
sel.to_a
-
end
-
-
1
def selector_comma_sequence
-
return unless sel = _selector
-
selectors = [sel]
-
ws = ''
-
while tok(/,/)
-
ws << str{ss}
-
if sel = _selector
-
selectors << sel
-
selectors[-1] = Selector::Sequence.new(["\n"] + selectors.last.members) if ws.include?("\n")
-
ws = ''
-
end
-
end
-
Selector::CommaSequence.new(selectors)
-
end
-
-
1
def _selector
-
# The combinator here allows the "> E" hack
-
return unless val = combinator || simple_selector_sequence
-
nl = str{ss}.include?("\n")
-
res = []
-
res << val
-
res << "\n" if nl
-
-
while val = combinator || simple_selector_sequence
-
res << val
-
res << "\n" if str{ss}.include?("\n")
-
end
-
Selector::Sequence.new(res.compact)
-
end
-
-
1
def combinator
-
tok(PLUS) || tok(GREATER) || tok(TILDE)
-
end
-
-
1
def simple_selector_sequence
-
# This allows for stuff like http://www.w3.org/TR/css3-animations/#keyframes-
-
return expr unless e = element_name || id_selector || class_selector ||
-
attrib || negation || pseudo || parent_selector || interpolation_selector
-
res = [e]
-
-
# The tok(/\*/) allows the "E*" hack
-
while v = id_selector || class_selector || attrib || negation || pseudo ||
-
interpolation_selector || (tok(/\*/) && Selector::Universal.new(nil))
-
res << v
-
end
-
-
pos = @scanner.pos
-
line = @line
-
if sel = str? {simple_selector_sequence}
-
@scanner.pos = pos
-
@line = line
-
-
if sel =~ /^&/
-
begin
-
throw_error {expected('"{"')}
-
rescue Sass::SyntaxError => e
-
e.message << "\n\n\"#{sel}\" may only be used at the beginning of a selector."
-
raise e
-
end
-
else
-
Sass::Util.sass_warn(<<MESSAGE)
-
DEPRECATION WARNING:
-
On line #{@line}#{" of \"#{@filename}\"" if @filename}, after "#{self.class.prior_snippet(@scanner)}"
-
Starting in Sass 3.2, "#{sel}" may only be used at the beginning of a selector.
-
MESSAGE
-
end
-
end
-
-
Selector::SimpleSequence.new(res)
-
end
-
-
1
def parent_selector
-
return unless tok(/&/)
-
Selector::Parent.new
-
end
-
-
1
def class_selector
-
return unless tok(/\./)
-
@expected = "class name"
-
Selector::Class.new(merge(expr!(:interp_ident)))
-
end
-
-
1
def id_selector
-
return unless tok(/#(?!\{)/)
-
@expected = "id name"
-
Selector::Id.new(merge(expr!(:interp_name)))
-
end
-
-
1
def element_name
-
return unless name = interp_ident || tok(/\*/) || (tok?(/\|/) && "")
-
if tok(/\|/)
-
@expected = "element name or *"
-
ns = name
-
name = interp_ident || tok!(/\*/)
-
end
-
-
if name == '*'
-
Selector::Universal.new(merge(ns))
-
else
-
Selector::Element.new(merge(name), merge(ns))
-
end
-
end
-
-
1
def interpolation_selector
-
return unless script = interpolation
-
Selector::Interpolation.new(script)
-
end
-
-
1
def attrib
-
return unless tok(/\[/)
-
ss
-
ns, name = attrib_name!
-
ss
-
-
if op = tok(/=/) ||
-
tok(INCLUDES) ||
-
tok(DASHMATCH) ||
-
tok(PREFIXMATCH) ||
-
tok(SUFFIXMATCH) ||
-
tok(SUBSTRINGMATCH)
-
@expected = "identifier or string"
-
ss
-
if val = tok(IDENT)
-
val = [val]
-
else
-
val = expr!(:interp_string)
-
end
-
ss
-
end
-
tok(/\]/)
-
-
Selector::Attribute.new(merge(name), merge(ns), op, merge(val))
-
end
-
-
1
def attrib_name!
-
if name_or_ns = interp_ident
-
# E, E|E
-
if tok(/\|(?!=)/)
-
ns = name_or_ns
-
name = interp_ident
-
else
-
name = name_or_ns
-
end
-
else
-
# *|E or |E
-
ns = [tok(/\*/) || ""]
-
tok!(/\|/)
-
name = expr!(:interp_ident)
-
end
-
return ns, name
-
end
-
-
1
def pseudo
-
return unless s = tok(/::?/)
-
@expected = "pseudoclass or pseudoelement"
-
name = expr!(:interp_ident)
-
if tok(/\(/)
-
ss
-
arg = expr!(:pseudo_expr)
-
tok!(/\)/)
-
end
-
Selector::Pseudo.new(s == ':' ? :class : :element, merge(name), merge(arg))
-
end
-
-
1
def pseudo_expr
-
return unless e = tok(PLUS) || tok(/-/) || tok(NUMBER) ||
-
interp_string || tok(IDENT) || interpolation
-
res = [e, str{ss}]
-
while e = tok(PLUS) || tok(/-/) || tok(NUMBER) ||
-
interp_string || tok(IDENT) || interpolation
-
res << e << str{ss}
-
end
-
res
-
end
-
-
1
def negation
-
return unless name = tok(NOT) || tok(MOZ_ANY)
-
ss
-
@expected = "selector"
-
sel = selector_comma_sequence
-
tok!(/\)/)
-
Selector::SelectorPseudoClass.new(name[1...-1], sel)
-
end
-
-
1
def declaration
-
# This allows the "*prop: val", ":prop: val", and ".prop: val" hacks
-
if s = tok(/[:\*\.]|\#(?!\{)/)
-
@use_property_exception = s !~ /[\.\#]/
-
name = [s, str{ss}, *expr!(:interp_ident)]
-
else
-
return unless name = interp_ident
-
name = [name] if name.is_a?(String)
-
end
-
if comment = tok(COMMENT)
-
name << comment
-
end
-
ss
-
-
tok!(/:/)
-
space, value = value!
-
ss
-
require_block = tok?(/\{/)
-
-
node = node(Sass::Tree::PropNode.new(name.flatten.compact, value, :new))
-
-
return node unless require_block
-
nested_properties! node, space
-
end
-
-
1
def value!
-
space = !str {ss}.empty?
-
@use_property_exception ||= space || !tok?(IDENT)
-
-
return true, Sass::Script::String.new("") if tok?(/\{/)
-
# This is a bit of a dirty trick:
-
# if the value is completely static,
-
# we don't parse it at all, and instead return a plain old string
-
# containing the value.
-
# This results in a dramatic speed increase.
-
if val = tok(STATIC_VALUE, true)
-
return space, Sass::Script::String.new(val.strip)
-
end
-
return space, sass_script(:parse)
-
end
-
-
1
def plain_value
-
return unless tok(/:/)
-
space = !str {ss}.empty?
-
@use_property_exception ||= space || !tok?(IDENT)
-
-
expression = expr
-
expression << tok(IMPORTANT) if expression
-
# expression, space, value
-
return expression, space, expression || [""]
-
end
-
-
1
def nested_properties!(node, space)
-
err(<<MESSAGE) unless space
-
Invalid CSS: a space is required between a property and its definition
-
when it has other properties nested beneath it.
-
MESSAGE
-
-
@use_property_exception = true
-
@expected = 'expression (e.g. 1px, bold) or "{"'
-
block(node, :property)
-
end
-
-
1
def expr
-
return unless t = term
-
res = [t, str{ss}]
-
-
while (o = operator) && (t = term)
-
res << o << t << str{ss}
-
end
-
-
res
-
end
-
-
1
def term
-
unless e = tok(NUMBER) ||
-
tok(URI) ||
-
function ||
-
tok(STRING) ||
-
tok(UNICODERANGE) ||
-
tok(IDENT) ||
-
tok(HEXCOLOR)
-
-
return unless op = unary_operator
-
@expected = "number or function"
-
return [op, tok(NUMBER) || expr!(:function)]
-
end
-
e
-
end
-
-
1
def function
-
return unless name = tok(FUNCTION)
-
if name == "expression(" || name == "calc("
-
str, _ = Sass::Shared.balance(@scanner, ?(, ?), 1)
-
[name, str]
-
else
-
[name, str{ss}, expr, tok!(/\)/)]
-
end
-
end
-
-
1
def interpolation
-
return unless tok(INTERP_START)
-
sass_script(:parse_interpolated)
-
end
-
-
1
def interp_string
-
_interp_string(:double) || _interp_string(:single)
-
end
-
-
1
def _interp_string(type)
-
return unless start = tok(Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[[type, false]])
-
res = [start]
-
-
mid_re = Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[[type, true]]
-
# @scanner[2].empty? means we've started an interpolated section
-
while @scanner[2] == '#{'
-
@scanner.pos -= 2 # Don't consume the #{
-
res.last.slice!(-2..-1)
-
res << expr!(:interpolation) << tok(mid_re)
-
end
-
res
-
end
-
-
1
def interp_ident(start = IDENT)
-
return unless val = tok(start) || interpolation || tok(IDENT_HYPHEN_INTERP, true)
-
res = [val]
-
while val = tok(NAME) || interpolation
-
res << val
-
end
-
res
-
end
-
-
1
def interp_name
-
interp_ident NAME
-
end
-
-
1
def str
-
@strs.push ""
-
yield
-
@strs.last
-
ensure
-
@strs.pop
-
end
-
-
1
def str?
-
pos = @scanner.pos
-
line = @line
-
@strs.push ""
-
throw_error {yield} && @strs.last
-
rescue Sass::SyntaxError => e
-
@scanner.pos = pos
-
@line = line
-
nil
-
ensure
-
@strs.pop
-
end
-
-
1
def node(node)
-
node.line = @line
-
node
-
end
-
-
1
@sass_script_parser = Class.new(Sass::Script::Parser)
-
1
@sass_script_parser.send(:include, ScriptParser)
-
# @private
-
1
def self.sass_script_parser; @sass_script_parser; end
-
-
1
def sass_script(*args)
-
parser = self.class.sass_script_parser.new(@scanner, @line,
-
@scanner.pos - (@scanner.string[0...@scanner.pos].rindex("\n") || 0))
-
result = parser.send(*args)
-
@line = parser.line
-
result
-
rescue Sass::SyntaxError => e
-
throw(:_sass_parser_error, true) if @throw_error
-
raise e
-
end
-
-
1
def merge(arr)
-
arr && Sass::Util.merge_adjacent_strings([arr].flatten)
-
end
-
-
1
EXPR_NAMES = {
-
:media_query => "media query (e.g. print, screen, print and screen)",
-
:media_expr => "media expression (e.g. (min-device-width: 800px)))",
-
:pseudo_expr => "expression (e.g. fr, 2n+1)",
-
:interp_ident => "identifier",
-
:interp_name => "identifier",
-
:expr => "expression (e.g. 1px, bold)",
-
:_selector => "selector",
-
:selector_comma_sequence => "selector",
-
:simple_selector_sequence => "selector",
-
:import_arg => "file to import (string or url())",
-
:moz_document_function => "matching function (e.g. url-prefix(), domain())",
-
:supports_condition => "@supports condition (e.g. (display: flexbox))",
-
:supports_condition_in_parens => "@supports condition (e.g. (display: flexbox))",
-
}
-
-
1
TOK_NAMES = Sass::Util.to_hash(
-
50
Sass::SCSS::RX.constants.map {|c| [Sass::SCSS::RX.const_get(c), c.downcase]}).
-
merge(IDENT => "identifier", /[;}]/ => '";"')
-
-
1
def tok?(rx)
-
@scanner.match?(rx)
-
end
-
-
1
def expr!(name)
-
(e = send(name)) && (return e)
-
expected(EXPR_NAMES[name] || name.to_s)
-
end
-
-
1
def tok!(rx)
-
(t = tok(rx)) && (return t)
-
name = TOK_NAMES[rx]
-
-
unless name
-
# Display basic regexps as plain old strings
-
string = rx.source.gsub(/\\(.)/, '\1')
-
name = rx.source == Regexp.escape(string) ? string.inspect : rx.inspect
-
end
-
-
expected(name)
-
end
-
-
1
def expected(name)
-
throw(:_sass_parser_error, true) if @throw_error
-
self.class.expected(@scanner, @expected || name, @line)
-
end
-
-
1
def err(msg)
-
throw(:_sass_parser_error, true) if @throw_error
-
raise Sass::SyntaxError.new(msg, :line => @line)
-
end
-
-
1
def throw_error
-
old_throw_error, @throw_error = @throw_error, false
-
yield
-
ensure
-
@throw_error = old_throw_error
-
end
-
-
1
def catch_error(&block)
-
old_throw_error, @throw_error = @throw_error, true
-
pos = @scanner.pos
-
line = @line
-
expected = @expected
-
if catch(:_sass_parser_error, &block)
-
@scanner.pos = pos
-
@line = line
-
@expected = expected
-
{:pos => pos, :line => line, :expected => @expected, :block => block}
-
end
-
ensure
-
@throw_error = old_throw_error
-
end
-
-
1
def rethrow(err)
-
if @throw_err
-
throw :_sass_parser_error, err
-
else
-
@scanner = Sass::Util::MultibyteStringScanner.new(@scanner.string)
-
@scanner.pos = err[:pos]
-
@line = err[:line]
-
@expected = err[:expected]
-
err[:block].call
-
end
-
end
-
-
# @private
-
1
def self.expected(scanner, expected, line)
-
was = scanner.rest.dup
-
# Get rid of whitespace between pos and the next token,
-
# but only if there's a newline in there
-
was.gsub!(/^\s*\n\s*/, '')
-
# Also get rid of stuff after the next newline
-
was.gsub!(/\n.*/, '')
-
was = was[0...15] + "..." if was.size > 18
-
-
raise Sass::SyntaxError.new(
-
"Invalid CSS after \"#{prior_snippet(scanner)}\": expected #{expected}, was \"#{was}\"",
-
:line => line)
-
end
-
-
# @private
-
1
def self.prior_snippet(scanner)
-
pos = scanner.pos
-
-
after = scanner.string[0...pos]
-
# Get rid of whitespace between pos and the last token,
-
# but only if there's a newline in there
-
after.gsub!(/\s*\n\s*$/, '')
-
# Also get rid of stuff before the last newline
-
after.gsub!(/.*\n/, '')
-
after = "..." + after[-15..-1] if after.size > 18
-
after
-
end
-
-
# Avoid allocating lots of new strings for `#tok`.
-
# This is important because `#tok` is called all the time.
-
1
NEWLINE = "\n"
-
-
1
def tok(rx, last_group_lookahead = false)
-
res = @scanner.scan(rx)
-
if res
-
# This fixes https://github.com/nex3/sass/issues/104, which affects
-
# Ruby 1.8.7 and REE. This fix is to replace the ?= zero-width
-
# positive lookahead operator in the Regexp (which matches without
-
# consuming the matched group), with a match that does consume the
-
# group, but then rewinds the scanner and removes the group from the
-
# end of the matched string. This fix makes the assumption that the
-
# matched group will always occur at the end of the match.
-
if last_group_lookahead && @scanner[-1]
-
@scanner.pos -= @scanner[-1].length
-
res.slice!(-@scanner[-1].length..-1)
-
end
-
@line += res.count(NEWLINE)
-
@expected = nil
-
if !@strs.empty? && rx != COMMENT && rx != SINGLE_LINE_COMMENT
-
@strs.each {|s| s << res}
-
end
-
res
-
end
-
end
-
end
-
end
-
end
-
1
module Sass
-
1
module SCSS
-
# A module containing regular expressions used
-
# for lexing tokens in an SCSS document.
-
# Most of these are taken from [the CSS3 spec](http://www.w3.org/TR/css3-syntax/#lexical),
-
# although some have been modified for various reasons.
-
1
module RX
-
# Takes a string and returns a CSS identifier
-
# that will have the value of the given string.
-
#
-
# @param str [String] The string to escape
-
# @return [String] The escaped string
-
1
def self.escape_ident(str)
-
return "" if str.empty?
-
return "\\#{str}" if str == '-' || str == '_'
-
out = ""
-
value = str.dup
-
out << value.slice!(0...1) if value =~ /^[-_]/
-
if value[0...1] =~ NMSTART
-
out << value.slice!(0...1)
-
else
-
out << escape_char(value.slice!(0...1))
-
end
-
out << value.gsub(/[^a-zA-Z0-9_-]/) {|c| escape_char c}
-
return out
-
end
-
-
# Escapes a single character for a CSS identifier.
-
#
-
# @param c [String] The character to escape. Should have length 1
-
# @return [String] The escaped character
-
# @private
-
1
def self.escape_char(c)
-
return "\\%06x" % Sass::Util.ord(c) unless c =~ /[ -\/:-~]/
-
return "\\#{c}"
-
end
-
-
# Creates a Regexp from a plain text string,
-
# escaping all significant characters.
-
#
-
# @param str [String] The text of the regexp
-
# @param flags [Fixnum] Flags for the created regular expression
-
# @return [Regexp]
-
# @private
-
1
def self.quote(str, flags = 0)
-
9
Regexp.new(Regexp.quote(str), flags)
-
end
-
-
1
H = /[0-9a-fA-F]/
-
1
NL = /\n|\r\n|\r|\f/
-
1
UNICODE = /\\#{H}{1,6}[ \t\r\n\f]?/
-
1
s = if Sass::Util.ruby1_8?
-
'\200-\377'
-
else
-
1
'\u{80}-\u{D7FF}\u{E000}-\u{FFFD}\u{10000}-\u{10FFFF}'
-
end
-
1
NONASCII = /[#{s}]/
-
1
ESCAPE = /#{UNICODE}|\\[ -~#{s}]/
-
1
NMSTART = /[_a-zA-Z]|#{NONASCII}|#{ESCAPE}/
-
1
NMCHAR = /[a-zA-Z0-9_-]|#{NONASCII}|#{ESCAPE}/
-
1
STRING1 = /\"((?:[^\n\r\f\\"]|\\#{NL}|#{ESCAPE})*)\"/
-
1
STRING2 = /\'((?:[^\n\r\f\\']|\\#{NL}|#{ESCAPE})*)\'/
-
-
1
IDENT = /-?#{NMSTART}#{NMCHAR}*/
-
1
NAME = /#{NMCHAR}+/
-
1
NUM = /[0-9]+|[0-9]*\.[0-9]+/
-
1
STRING = /#{STRING1}|#{STRING2}/
-
1
URLCHAR = /[#%&*-~]|#{NONASCII}|#{ESCAPE}/
-
1
URL = /(#{URLCHAR}*)/
-
1
W = /[ \t\r\n\f]*/
-
1
VARIABLE = /(\$)(#{Sass::SCSS::RX::IDENT})/
-
-
# This is more liberal than the spec's definition,
-
# but that definition didn't work well with the greediness rules
-
1
RANGE = /(?:#{H}|\?){1,6}/
-
-
##
-
-
1
S = /[ \t\r\n\f]+/
-
-
1
COMMENT = /\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\//
-
1
SINGLE_LINE_COMMENT = /\/\/.*(\n[ \t]*\/\/.*)*/
-
-
1
CDO = quote("<!--")
-
1
CDC = quote("-->")
-
1
INCLUDES = quote("~=")
-
1
DASHMATCH = quote("|=")
-
1
PREFIXMATCH = quote("^=")
-
1
SUFFIXMATCH = quote("$=")
-
1
SUBSTRINGMATCH = quote("*=")
-
-
1
HASH = /##{NAME}/
-
-
1
IMPORTANT = /!#{W}important/i
-
1
DEFAULT = /!#{W}default/i
-
-
1
NUMBER = /#{NUM}(?:#{IDENT}|%)?/
-
-
1
URI = /url\(#{W}(?:#{STRING}|#{URL})#{W}\)/i
-
1
FUNCTION = /#{IDENT}\(/
-
-
1
UNICODERANGE = /u\+(?:#{H}{1,6}-#{H}{1,6}|#{RANGE})/i
-
-
# Defined in http://www.w3.org/TR/css3-selectors/#lex
-
1
PLUS = /#{W}\+/
-
1
GREATER = /#{W}>/
-
1
TILDE = /#{W}~/
-
1
NOT = quote(":not(", Regexp::IGNORECASE)
-
-
# Defined in https://developer.mozilla.org/en/CSS/@-moz-document as a
-
# non-standard version of http://www.w3.org/TR/css3-conditional/
-
1
URL_PREFIX = /url-prefix\(#{W}(?:#{STRING}|#{URL})#{W}\)/i
-
1
DOMAIN = /domain\(#{W}(?:#{STRING}|#{URL})#{W}\)/i
-
-
# Custom
-
1
HEXCOLOR = /\#[0-9a-fA-F]+/
-
1
INTERP_START = /#\{/
-
1
MOZ_ANY = quote(":-moz-any(", Regexp::IGNORECASE)
-
-
1
IDENT_HYPHEN_INTERP = /-(#\{)/
-
1
STRING1_NOINTERP = /\"((?:[^\n\r\f\\"#]|#(?!\{)|\\#{NL}|#{ESCAPE})*)\"/
-
1
STRING2_NOINTERP = /\'((?:[^\n\r\f\\'#]|#(?!\{)|\\#{NL}|#{ESCAPE})*)\'/
-
1
STRING_NOINTERP = /#{STRING1_NOINTERP}|#{STRING2_NOINTERP}/
-
# Can't use IDENT here, because it seems to take exponential time on 1.8.
-
# We could use it for 1.9 only, but I don't want to introduce a cross-version
-
# behavior difference.
-
# In any case, almost all CSS idents will be matched by this.
-
#
-
# We explicitly avoid parsing newlines or values/selectors longer than
-
# about 50 characters. This mitigates the problem of exponential parsing
-
# time when a value has a long string of valid, parsable content followed
-
# by something invalid.
-
1
STATIC_VALUE = /(-?#{NMSTART}|#{STRING_NOINTERP}|[ \t](?!%)|#[a-f0-9]|[,%]|#{NUM}|\!important){0,50}([;}])/i
-
1
STATIC_SELECTOR = /(#{NMCHAR}|[ \t]|[,>+*]|[:#.]#{NMSTART}){0,50}([{])/i
-
end
-
end
-
end
-
1
module Sass
-
1
module SCSS
-
# A subclass of {Parser} that parses code in Sass documents
-
# using some SCSS constructs.
-
# This is necessary because SassScript in Sass supports `!`-style variables,
-
# whereas in SCSS it doesn't.
-
1
class SassParser < Parser
-
1
@sass_script_parser = Sass::Script::Parser
-
end
-
end
-
end
-
1
module Sass
-
1
module SCSS
-
# A mixin for subclasses of {Sass::Script::Lexer}
-
# that makes them usable by {SCSS::Parser} to parse SassScript.
-
# In particular, the lexer doesn't support `!` for a variable prefix.
-
1
module ScriptLexer
-
1
private
-
-
1
def variable
-
return [:raw, "!important"] if scan(Sass::SCSS::RX::IMPORTANT)
-
_variable(Sass::SCSS::RX::VARIABLE)
-
end
-
end
-
end
-
end
-
1
module Sass
-
1
module SCSS
-
# A mixin for subclasses of {Sass::Script::Parser}
-
# that makes them usable by {SCSS::Parser} to parse SassScript.
-
# In particular, the parser won't raise an error
-
# when there's more content in the lexer once lexing is done.
-
# In addition, the parser doesn't support `!` for a variable prefix.
-
1
module ScriptParser
-
1
private
-
-
# @private
-
1
def lexer_class
-
klass = Class.new(super)
-
klass.send(:include, ScriptLexer)
-
klass
-
end
-
-
# Instead of raising an error when the parser is done,
-
# rewind the StringScanner so that it hasn't consumed the final token.
-
1
def assert_done
-
@lexer.unpeek!
-
end
-
end
-
end
-
end
-
1
module Sass
-
1
module SCSS
-
# A parser for a static SCSS tree.
-
# Parses with SCSS extensions, like nested rules and parent selectors,
-
# but without dynamic SassScript.
-
# This is useful for e.g. \{#parse\_selector parsing selectors}
-
# after resolving the interpolation.
-
1
class StaticParser < Parser
-
# Parses the text as a selector.
-
#
-
# @param filename [String, nil] The file in which the selector appears,
-
# or nil if there is no such file.
-
# Used for error reporting.
-
# @return [Selector::CommaSequence] The parsed selector
-
# @raise [Sass::SyntaxError] if there's a syntax error in the selector
-
1
def parse_selector
-
init_scanner!
-
seq = expr!(:selector_comma_sequence)
-
expected("selector") unless @scanner.eos?
-
seq.line = @line
-
seq.filename = @filename
-
seq
-
end
-
-
1
private
-
-
1
def variable; nil; end
-
1
def script_value; nil; end
-
1
def interpolation; nil; end
-
1
def interp_string; s = tok(STRING) and [s]; end
-
1
def interp_ident(ident = IDENT); s = tok(ident) and [s]; end
-
1
def use_css_import?; true; end
-
-
1
def special_directive(name)
-
return unless %w[media import charset -moz-document].include?(name)
-
super
-
end
-
end
-
end
-
end
-
1
require 'sass/selector/simple'
-
1
require 'sass/selector/abstract_sequence'
-
1
require 'sass/selector/comma_sequence'
-
1
require 'sass/selector/sequence'
-
1
require 'sass/selector/simple_sequence'
-
-
1
module Sass
-
# A namespace for nodes in the parse tree for selectors.
-
#
-
# {CommaSequence} is the toplevel seelctor,
-
# representing a comma-separated sequence of {Sequence}s,
-
# such as `foo bar, baz bang`.
-
# {Sequence} is the next level,
-
# representing {SimpleSequence}s separated by combinators (e.g. descendant or child),
-
# such as `foo bar` or `foo > bar baz`.
-
# {SimpleSequence} is a sequence of selectors that all apply to a single element,
-
# such as `foo.bar[attr=val]`.
-
# Finally, {Simple} is the superclass of the simplest selectors,
-
# such as `.foo` or `#bar`.
-
1
module Selector
-
# A parent-referencing selector (`&` in Sass).
-
# The function of this is to be replaced by the parent selector
-
# in the nested hierarchy.
-
1
class Parent < Simple
-
# @see Selector#to_a
-
1
def to_a
-
["&"]
-
end
-
-
# Always raises an exception.
-
#
-
# @raise [Sass::SyntaxError] Parent selectors should be resolved before unification
-
# @see Selector#unify
-
1
def unify(sels)
-
raise Sass::SyntaxError.new("[BUG] Cannot unify parent selectors.")
-
end
-
end
-
-
# A class selector (e.g. `.foo`).
-
1
class Class < Simple
-
# The class name.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_reader :name
-
-
# @param name [Array<String, Sass::Script::Node>] The class name
-
1
def initialize(name)
-
@name = name
-
end
-
-
# @see Selector#to_a
-
1
def to_a
-
[".", *@name]
-
end
-
end
-
-
# An id selector (e.g. `#foo`).
-
1
class Id < Simple
-
# The id name.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_reader :name
-
-
# @param name [Array<String, Sass::Script::Node>] The id name
-
1
def initialize(name)
-
@name = name
-
end
-
-
# @see Selector#to_a
-
1
def to_a
-
["#", *@name]
-
end
-
-
# Returns `nil` if `sels` contains an {Id} selector
-
# with a different name than this one.
-
#
-
# @see Selector#unify
-
1
def unify(sels)
-
return if sels.any? {|sel2| sel2.is_a?(Id) && self.name != sel2.name}
-
super
-
end
-
end
-
-
# A universal selector (`*` in CSS).
-
1
class Universal < Simple
-
# The selector namespace.
-
# `nil` means the default namespace,
-
# `[""]` means no namespace,
-
# `["*"]` means any namespace.
-
#
-
# @return [Array<String, Sass::Script::Node>, nil]
-
1
attr_reader :namespace
-
-
# @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
-
1
def initialize(namespace)
-
@namespace = namespace
-
end
-
-
# @see Selector#to_a
-
1
def to_a
-
@namespace ? @namespace + ["|*"] : ["*"]
-
end
-
-
# Unification of a universal selector is somewhat complicated,
-
# especially when a namespace is specified.
-
# If there is no namespace specified
-
# or any namespace is specified (namespace `"*"`),
-
# then `sel` is returned without change
-
# (unless it's empty, in which case `"*"` is required).
-
#
-
# If a namespace is specified
-
# but `sel` does not specify a namespace,
-
# then the given namespace is applied to `sel`,
-
# either by adding this {Universal} selector
-
# or applying this namespace to an existing {Element} selector.
-
#
-
# If both this selector *and* `sel` specify namespaces,
-
# those namespaces are unified via {Simple#unify_namespaces}
-
# and the unified namespace is used, if possible.
-
#
-
# @todo There are lots of cases that this documentation specifies;
-
# make sure we thoroughly test **all of them**.
-
# @todo Keep track of whether a default namespace has been declared
-
# and handle namespace-unspecified selectors accordingly.
-
# @todo If any branch of a CommaSequence ends up being just `"*"`,
-
# then all other branches should be eliminated
-
#
-
# @see Selector#unify
-
1
def unify(sels)
-
name =
-
case sels.first
-
when Universal; :universal
-
when Element; sels.first.name
-
else
-
return [self] + sels unless namespace.nil? || namespace == ['*']
-
return sels unless sels.empty?
-
return [self]
-
end
-
-
ns, accept = unify_namespaces(namespace, sels.first.namespace)
-
return unless accept
-
[name == :universal ? Universal.new(ns) : Element.new(name, ns)] + sels[1..-1]
-
end
-
end
-
-
# An element selector (e.g. `h1`).
-
1
class Element < Simple
-
# The element name.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_reader :name
-
-
# The selector namespace.
-
# `nil` means the default namespace,
-
# `[""]` means no namespace,
-
# `["*"]` means any namespace.
-
#
-
# @return [Array<String, Sass::Script::Node>, nil]
-
1
attr_reader :namespace
-
-
# @param name [Array<String, Sass::Script::Node>] The element name
-
# @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
-
1
def initialize(name, namespace)
-
@name = name
-
@namespace = namespace
-
end
-
-
# @see Selector#to_a
-
1
def to_a
-
@namespace ? @namespace + ["|"] + @name : @name
-
end
-
-
# Unification of an element selector is somewhat complicated,
-
# especially when a namespace is specified.
-
# First, if `sel` contains another {Element} with a different \{#name},
-
# then the selectors can't be unified and `nil` is returned.
-
#
-
# Otherwise, if `sel` doesn't specify a namespace,
-
# or it specifies any namespace (via `"*"`),
-
# then it's returned with this element selector
-
# (e.g. `.foo` becomes `a.foo` or `svg|a.foo`).
-
# Similarly, if this selector doesn't specify a namespace,
-
# the namespace from `sel` is used.
-
#
-
# If both this selector *and* `sel` specify namespaces,
-
# those namespaces are unified via {Simple#unify_namespaces}
-
# and the unified namespace is used, if possible.
-
#
-
# @todo There are lots of cases that this documentation specifies;
-
# make sure we thoroughly test **all of them**.
-
# @todo Keep track of whether a default namespace has been declared
-
# and handle namespace-unspecified selectors accordingly.
-
#
-
# @see Selector#unify
-
1
def unify(sels)
-
case sels.first
-
when Universal;
-
when Element; return unless name == sels.first.name
-
else return [self] + sels
-
end
-
-
ns, accept = unify_namespaces(namespace, sels.first.namespace)
-
return unless accept
-
[Element.new(name, ns)] + sels[1..-1]
-
end
-
end
-
-
# Selector interpolation (`#{}` in Sass).
-
1
class Interpolation < Simple
-
# The script to run.
-
#
-
# @return [Sass::Script::Node]
-
1
attr_reader :script
-
-
# @param script [Sass::Script::Node] The script to run
-
1
def initialize(script)
-
@script = script
-
end
-
-
# @see Selector#to_a
-
1
def to_a
-
[@script]
-
end
-
-
# Always raises an exception.
-
#
-
# @raise [Sass::SyntaxError] Interpolation selectors should be resolved before unification
-
# @see Selector#unify
-
1
def unify(sels)
-
raise Sass::SyntaxError.new("[BUG] Cannot unify interpolation selectors.")
-
end
-
end
-
-
# An attribute selector (e.g. `[href^="http://"]`).
-
1
class Attribute < Simple
-
# The attribute name.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_reader :name
-
-
# The attribute namespace.
-
# `nil` means the default namespace,
-
# `[""]` means no namespace,
-
# `["*"]` means any namespace.
-
#
-
# @return [Array<String, Sass::Script::Node>, nil]
-
1
attr_reader :namespace
-
-
# The matching operator, e.g. `"="` or `"^="`.
-
#
-
# @return [String]
-
1
attr_reader :operator
-
-
# The right-hand side of the operator.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_reader :value
-
-
# @param name [Array<String, Sass::Script::Node>] The attribute name
-
# @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
-
# @param operator [String] The matching operator, e.g. `"="` or `"^="`
-
# @param value [Array<String, Sass::Script::Node>] See \{#value}
-
1
def initialize(name, namespace, operator, value)
-
@name = name
-
@namespace = namespace
-
@operator = operator
-
@value = value
-
end
-
-
# @see Selector#to_a
-
1
def to_a
-
res = ["["]
-
res.concat(@namespace) << "|" if @namespace
-
res.concat @name
-
(res << @operator).concat @value if @value
-
res << "]"
-
end
-
end
-
-
# A pseudoclass (e.g. `:visited`) or pseudoelement (e.g. `::first-line`) selector.
-
# It can have arguments (e.g. `:nth-child(2n+1)`).
-
1
class Pseudo < Simple
-
# The type of the selector.
-
# `:class` if this is a pseudoclass selector,
-
# `:element` if it's a pseudoelement.
-
#
-
# @return [Symbol]
-
1
attr_reader :type
-
-
# Some psuedo-class-syntax selectors (`:after` and `:before)
-
# are actually considered pseudo-elements
-
# and must be at the end of the selector to function properly.
-
#
-
# @return [Array<String>]
-
1
FINAL_SELECTORS = %w[after before]
-
-
# The name of the selector.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_reader :name
-
-
# The argument to the selector,
-
# or `nil` if no argument was given.
-
#
-
# This may include SassScript nodes that will be run during resolution.
-
# Note that this should not include SassScript nodes
-
# after resolution has taken place.
-
#
-
# @return [Array<String, Sass::Script::Node>, nil]
-
1
attr_reader :arg
-
-
# @param type [Symbol] See \{#type}
-
# @param name [Array<String, Sass::Script::Node>] The name of the selector
-
# @param arg [nil, Array<String, Sass::Script::Node>] The argument to the selector,
-
# or nil if no argument was given
-
1
def initialize(type, name, arg)
-
@type = type
-
@name = name
-
@arg = arg
-
end
-
-
1
def final?
-
type == :class && FINAL_SELECTORS.include?(name.first)
-
end
-
-
# @see Selector#to_a
-
1
def to_a
-
res = [@type == :class ? ":" : "::"] + @name
-
(res << "(").concat(Sass::Util.strip_string_array(@arg)) << ")" if @arg
-
res
-
end
-
-
# Returns `nil` if this is a pseudoelement selector
-
# and `sels` contains a pseudoelement selector different than this one.
-
#
-
# @see Selector#unify
-
1
def unify(sels)
-
return if type == :element && sels.any? do |sel|
-
sel.is_a?(Pseudo) && sel.type == :element &&
-
(sel.name != self.name || sel.arg != self.arg)
-
end
-
return sels + [self] if final?
-
super
-
end
-
end
-
-
# A pseudoclass selector whose argument is itself a selector
-
# (e.g. `:not(.foo)` or `:-moz-all(.foo, .bar)`).
-
1
class SelectorPseudoClass < Simple
-
# The name of the pseudoclass.
-
#
-
# @return [String]
-
1
attr_reader :name
-
-
# The selector argument.
-
#
-
# @return [Selector::Sequence]
-
1
attr_reader :selector
-
-
# @param [String] The name of the pseudoclass
-
# @param [Selector::CommaSequence] The selector argument
-
1
def initialize(name, selector)
-
@name = name
-
@selector = selector
-
end
-
-
# @see Selector#to_a
-
1
def to_a
-
[":", @name, "("] + @selector.to_a + [")"]
-
end
-
end
-
end
-
end
-
1
module Sass
-
1
module Selector
-
# The abstract parent class of the various selector sequence classes.
-
#
-
# All subclasses should implement a `members` method that returns an array
-
# of object that respond to `#line=` and `#filename=`, as well as a `to_a`
-
# method that returns an array of strings and script nodes.
-
1
class AbstractSequence
-
# The line of the Sass template on which this selector was declared.
-
#
-
# @return [Fixnum]
-
1
attr_reader :line
-
-
# The name of the file in which this selector was declared.
-
#
-
# @return [String, nil]
-
1
attr_reader :filename
-
-
# Sets the line of the Sass template on which this selector was declared.
-
# This also sets the line for all child selectors.
-
#
-
# @param line [Fixnum]
-
# @return [Fixnum]
-
1
def line=(line)
-
members.each {|m| m.line = line}
-
@line = line
-
end
-
-
# Sets the name of the file in which this selector was declared,
-
# or `nil` if it was not declared in a file (e.g. on stdin).
-
# This also sets the filename for all child selectors.
-
#
-
# @param filename [String, nil]
-
# @return [String, nil]
-
1
def filename=(filename)
-
members.each {|m| m.filename = filename}
-
@filename = filename
-
end
-
-
# Returns a hash code for this sequence.
-
#
-
# Subclasses should define `#_hash` rather than overriding this method,
-
# which automatically handles memoizing the result.
-
#
-
# @return [Fixnum]
-
1
def hash
-
@_hash ||= _hash
-
end
-
-
# Checks equality between this and another object.
-
#
-
# Subclasses should define `#_eql?` rather than overriding this method,
-
# which handles checking class equality and hash equality.
-
#
-
# @param other [Object] The object to test equality against
-
# @return [Boolean] Whether or not this is equal to `other`
-
1
def eql?(other)
-
other.class == self.class && other.hash == self.hash && _eql?(other)
-
end
-
1
alias_method :==, :eql?
-
-
# Converts the selector into a string. This is the standard selector
-
# string, along with any SassScript interpolation that may exist.
-
#
-
# @return [String]
-
1
def to_s
-
to_a.map {|e| e.is_a?(Sass::Script::Node) ? "\#{#{e.to_sass}}" : e}.join
-
end
-
end
-
end
-
end
-
1
module Sass
-
1
module Selector
-
# A comma-separated sequence of selectors.
-
1
class CommaSequence < AbstractSequence
-
# The comma-separated selector sequences
-
# represented by this class.
-
#
-
# @return [Array<Sequence>]
-
1
attr_reader :members
-
-
# @param seqs [Array<Sequence>] See \{#members}
-
1
def initialize(seqs)
-
@members = seqs
-
end
-
-
# Resolves the {Parent} selectors within this selector
-
# by replacing them with the given parent selector,
-
# handling commas appropriately.
-
#
-
# @param super_cseq [CommaSequence] The parent selector
-
# @return [CommaSequence] This selector, with parent references resolved
-
# @raise [Sass::SyntaxError] If a parent selector is invalid
-
1
def resolve_parent_refs(super_cseq)
-
if super_cseq.nil?
-
if @members.any? do |sel|
-
sel.members.any? do |sel_or_op|
-
sel_or_op.is_a?(SimpleSequence) && sel_or_op.members.any? {|ssel| ssel.is_a?(Parent)}
-
end
-
end
-
raise Sass::SyntaxError.new("Base-level rules cannot contain the parent-selector-referencing character '&'.")
-
end
-
return self
-
end
-
-
CommaSequence.new(
-
super_cseq.members.map do |super_seq|
-
@members.map {|seq| seq.resolve_parent_refs(super_seq)}
-
end.flatten)
-
end
-
-
# Non-destrucively extends this selector with the extensions specified in a hash
-
# (which should come from {Sass::Tree::Visitors::Cssize}).
-
#
-
# @todo Link this to the reference documentation on `@extend`
-
# when such a thing exists.
-
#
-
# @param extends [Sass::Util::SubsetMap{Selector::Simple => Selector::Sequence}]
-
# The extensions to perform on this selector
-
# @return [CommaSequence] A copy of this selector,
-
# with extensions made according to `extends`
-
1
def do_extend(extends)
-
CommaSequence.new(members.map {|seq| seq.do_extend(extends)}.flatten)
-
end
-
-
# Returns a string representation of the sequence.
-
# This is basically the selector string.
-
#
-
# @return [String]
-
1
def inspect
-
members.map {|m| m.inspect}.join(", ")
-
end
-
-
# @see Simple#to_a
-
1
def to_a
-
arr = Sass::Util.intersperse(@members.map {|m| m.to_a}, ", ").flatten
-
arr.delete("\n")
-
arr
-
end
-
-
1
private
-
-
1
def _hash
-
members.hash
-
end
-
-
1
def _eql?(other)
-
other.class == self.class && other.members.eql?(self.members)
-
end
-
end
-
end
-
end
-
1
module Sass
-
1
module Selector
-
# An operator-separated sequence of
-
# {SimpleSequence simple selector sequences}.
-
1
class Sequence < AbstractSequence
-
# Sets the line of the Sass template on which this selector was declared.
-
# This also sets the line for all child selectors.
-
#
-
# @param line [Fixnum]
-
# @return [Fixnum]
-
1
def line=(line)
-
members.each {|m| m.line = line if m.is_a?(SimpleSequence)}
-
line
-
end
-
-
# Sets the name of the file in which this selector was declared,
-
# or `nil` if it was not declared in a file (e.g. on stdin).
-
# This also sets the filename for all child selectors.
-
#
-
# @param filename [String, nil]
-
# @return [String, nil]
-
1
def filename=(filename)
-
members.each {|m| m.filename = filename if m.is_a?(SimpleSequence)}
-
filename
-
end
-
-
# The array of {SimpleSequence simple selector sequences}, operators, and newlines.
-
# The operators are strings such as `"+"` and `">"`
-
# representing the corresponding CSS operators.
-
# Newlines are also newline strings;
-
# these aren't semantically relevant,
-
# but they do affect formatting.
-
#
-
# @return [Array<SimpleSequence, String>]
-
1
attr_reader :members
-
-
# @param seqs_and_ops [Array<SimpleSequence, String>] See \{#members}
-
1
def initialize(seqs_and_ops)
-
@members = seqs_and_ops
-
end
-
-
# Resolves the {Parent} selectors within this selector
-
# by replacing them with the given parent selector,
-
# handling commas appropriately.
-
#
-
# @param super_seq [Sequence] The parent selector sequence
-
# @return [Sequence] This selector, with parent references resolved
-
# @raise [Sass::SyntaxError] If a parent selector is invalid
-
1
def resolve_parent_refs(super_seq)
-
members = @members
-
nl = (members.first == "\n" && members.shift)
-
unless members.any? do |seq_or_op|
-
seq_or_op.is_a?(SimpleSequence) && seq_or_op.members.first.is_a?(Parent)
-
end
-
members = []
-
members << nl if nl
-
members << SimpleSequence.new([Parent.new])
-
members += @members
-
end
-
-
Sequence.new(
-
members.map do |seq_or_op|
-
next seq_or_op unless seq_or_op.is_a?(SimpleSequence)
-
seq_or_op.resolve_parent_refs(super_seq)
-
end.flatten)
-
end
-
-
# Non-destructively extends this selector with the extensions specified in a hash
-
# (which should come from {Sass::Tree::Visitors::Cssize}).
-
#
-
# @overload def do_extend(extends)
-
# @param extends [Sass::Util::SubsetMap{Selector::Simple => Selector::Sequence}]
-
# The extensions to perform on this selector
-
# @return [Array<Sequence>] A list of selectors generated
-
# by extending this selector with `extends`.
-
# These correspond to a {CommaSequence}'s {CommaSequence#members members array}.
-
# @see CommaSequence#do_extend
-
1
def do_extend(extends, seen = Set.new)
-
paths = Sass::Util.paths(members.map do |sseq_or_op|
-
next [[sseq_or_op]] unless sseq_or_op.is_a?(SimpleSequence)
-
extended = sseq_or_op.do_extend(extends, seen)
-
choices = extended.map {|seq| seq.members}
-
choices.unshift([sseq_or_op]) unless extended.any? {|seq| seq.superselector?(sseq_or_op)}
-
choices
-
end)
-
Sass::Util.flatten(paths.map {|path| weave(path)}, 1).map {|p| Sequence.new(p)}
-
end
-
-
# Returns whether or not this selector matches all elements
-
# that the given selector matches (as well as possibly more).
-
#
-
# @example
-
# (.foo).superselector?(.foo.bar) #=> true
-
# (.foo).superselector?(.bar) #=> false
-
# (.bar .foo).superselector?(.foo) #=> false
-
# @param sseq [SimpleSequence]
-
# @return [Boolean]
-
1
def superselector?(sseq)
-
return false unless members.size == 1
-
members.last.superselector?(sseq)
-
end
-
-
# @see Simple#to_a
-
1
def to_a
-
ary = @members.map {|seq_or_op| seq_or_op.is_a?(SimpleSequence) ? seq_or_op.to_a : seq_or_op}
-
Sass::Util.intersperse(ary, " ").flatten.compact
-
end
-
-
# Returns a string representation of the sequence.
-
# This is basically the selector string.
-
#
-
# @return [String]
-
1
def inspect
-
members.map {|m| m.inspect}.join(" ")
-
end
-
-
1
private
-
-
# Conceptually, this expands "parenthesized selectors".
-
# That is, if we have `.A .B {@extend .C}` and `.D .C {...}`,
-
# this conceptually expands into `.D .C, .D (.A .B)`,
-
# and this function translates `.D (.A .B)` into `.D .A .B, .A.D .B, .D .A .B`.
-
#
-
# @param path [Array<Array<SimpleSequence or String>>] A list of parenthesized selector groups.
-
# @return [Array<Array<SimpleSequence or String>>] A list of fully-expanded selectors.
-
1
def weave(path)
-
befores = [[]]
-
afters = path.dup
-
-
until afters.empty?
-
current = afters.shift.dup
-
last_current = [current.pop]
-
while !current.empty? && last_current.first.is_a?(String) || current.last.is_a?(String)
-
last_current.unshift(current.pop)
-
end
-
befores = Sass::Util.flatten(befores.map do |before|
-
next [] unless sub = subweave(before, current)
-
sub.map {|seqs| seqs + last_current}
-
end, 1)
-
end
-
return befores
-
end
-
-
# This interweaves two lists of selectors,
-
# returning all possible orderings of them (including using unification)
-
# that maintain the relative ordering of the input arrays.
-
#
-
# For example, given `.foo .bar` and `.baz .bang`,
-
# this would return `.foo .bar .baz .bang`, `.foo .bar.baz .bang`,
-
# `.foo .baz .bar .bang`, `.foo .baz .bar.bang`, `.foo .baz .bang .bar`,
-
# and so on until `.baz .bang .foo .bar`.
-
#
-
# @param seq1 [Array<SimpleSequence or String>]
-
# @param seq2 [Array<SimpleSequence or String>]
-
# @return [Array<Array<SimpleSequence or String>>]
-
1
def subweave(seq1, seq2)
-
return [seq2] if seq1.empty?
-
return [seq1] if seq2.empty?
-
-
return unless init = merge_initial_ops(seq1, seq2)
-
seq1 = group_selectors(seq1)
-
seq2 = group_selectors(seq2)
-
lcs = Sass::Util.lcs(seq2, seq1) do |s1, s2|
-
next s1 if s1 == s2
-
next unless s1.first.is_a?(SimpleSequence) && s2.first.is_a?(SimpleSequence)
-
next s2 if subweave_superselector?(s1, s2)
-
next s1 if subweave_superselector?(s2, s1)
-
end
-
-
diff = [[init]]
-
until lcs.empty?
-
diff << chunks(seq1, seq2) {|s| subweave_superselector?(s.first, lcs.first)} << [lcs.shift]
-
seq1.shift
-
seq2.shift
-
end
-
diff << chunks(seq1, seq2) {|s| s.empty?}
-
diff.reject! {|c| c.empty?}
-
-
Sass::Util.paths(diff).map {|p| p.flatten}
-
end
-
-
# Extracts initial selector operators (`"+"`, `">"`, `"~"`, and `"\n"`)
-
# from two sequences and merges them together into a single array of
-
# selector operators.
-
#
-
# @param seq1 [Array<SimpleSequence or String>]
-
# @param seq2 [Array<SimpleSequence or String>]
-
# @return [Array<String>, nil] If there are no operators in the merged
-
# sequence, this will be the empty array. If the operators cannot be
-
# merged, this will be nil.
-
1
def merge_initial_ops(seq1, seq2)
-
ops1, ops2 = [], []
-
ops1 << seq1.shift while seq1.first.is_a?(String)
-
ops2 << seq2.shift while seq2.first.is_a?(String)
-
-
newline = false
-
newline ||= !!ops1.shift if ops1.first == "\n"
-
newline ||= !!ops2.shift if ops2.first == "\n"
-
-
# If neither sequence is a subsequence of the other, they cannot be
-
# merged successfully
-
lcs = Sass::Util.lcs(ops1, ops2)
-
return unless lcs == ops1 || lcs == ops2
-
return (newline ? ["\n"] : []) + (ops1.size > ops2.size ? ops1 : ops2)
-
end
-
-
# Takes initial subsequences of `seq1` and `seq2` and returns all
-
# orderings of those subsequences. The initial subsequences are determined
-
# by a block.
-
#
-
# Destructively removes the initial subsequences of `seq1` and `seq2`.
-
#
-
# For example, given `(A B C | D E)` and `(1 2 | 3 4 5)` (with `|`
-
# denoting the boundary of the initial subsequence), this would return
-
# `[(A B C 1 2), (1 2 A B C)]`. The sequences would then be `(D E)` and
-
# `(3 4 5)`.
-
#
-
# @param seq1 [Array]
-
# @param seq2 [Array]
-
# @yield [a] Used to determine when to cut off the initial subsequences.
-
# Called repeatedly for each sequence until it returns true.
-
# @yieldparam a [Array] A final subsequence of one input sequence after
-
# cutting off some initial subsequence.
-
# @yieldreturn [Boolean] Whether or not to cut off the initial subsequence
-
# here.
-
1
def chunks(seq1, seq2)
-
chunk1 = []
-
chunk1 << seq1.shift until yield seq1
-
chunk2 = []
-
chunk2 << seq2.shift until yield seq2
-
return [] if chunk1.empty? && chunk2.empty?
-
return [chunk2] if chunk1.empty?
-
return [chunk1] if chunk2.empty?
-
[chunk1 + chunk2, chunk2 + chunk1]
-
end
-
-
# Groups a sequence into subsequences. The subsequences are determined by
-
# strings; adjacent non-string elements will be put into separate groups,
-
# but any element adjacent to a string will be grouped with that string.
-
#
-
# For example, `(A B "C" D E "F" G "H" "I" J)` will become `[(A) (B "C" D)
-
# (E "F" G "H" "I" J)]`.
-
#
-
# @param seq [Array]
-
# @return [Array<Array>]
-
1
def group_selectors(seq)
-
newseq = []
-
tail = seq.dup
-
until tail.empty?
-
head = []
-
begin
-
head << tail.shift
-
end while !tail.empty? && head.last.is_a?(String) || tail.first.is_a?(String)
-
newseq << head
-
end
-
return newseq
-
end
-
-
# Given two sequences of simple selectors, returns whether `sseq1` is a
-
# superselector of `sseq2`.
-
#
-
# @param sseq1 [Array<SimpleSelector or String>]
-
# @param sseq2 [Array<SimpleSelector or String>]
-
# @return [Boolean]
-
1
def subweave_superselector?(sseq1, sseq2)
-
if sseq1.size > 1
-
# More complex selectors are never superselectors of less complex ones
-
return unless sseq2.size > 1
-
# .foo ~ .bar is a superselector of .foo + .bar
-
return unless sseq1[1] == "~" ? sseq2[1] != ">" : sseq2[1] == sseq1[1]
-
return unless sseq1.first.superselector?(sseq2.first)
-
return true if sseq1.size == 2
-
return false if sseq2.size == 2
-
return subweave_superselector?(sseq1[2..-1], sseq2[2..-1])
-
elsif sseq2.size > 1
-
return true if sseq2[1] == ">" && sseq1.first.superselector?(sseq2.first)
-
return false if sseq2.size == 2
-
return subweave_superselector?(sseq1, sseq2[2..-1])
-
else
-
sseq1.first.superselector?(sseq2.first)
-
end
-
end
-
-
1
def _hash
-
members.reject {|m| m == "\n"}.hash
-
end
-
-
1
def _eql?(other)
-
other.members.reject {|m| m == "\n"}.eql?(self.members.reject {|m| m == "\n"})
-
end
-
end
-
end
-
end
-
1
module Sass
-
1
module Selector
-
# The abstract superclass for simple selectors
-
# (that is, those that don't compose multiple selectors).
-
1
class Simple
-
# The line of the Sass template on which this selector was declared.
-
#
-
# @return [Fixnum]
-
1
attr_accessor :line
-
-
# The name of the file in which this selector was declared,
-
# or `nil` if it was not declared in a file (e.g. on stdin).
-
#
-
# @return [String, nil]
-
1
attr_accessor :filename
-
-
# Returns a representation of the node
-
# as an array of strings and potentially {Sass::Script::Node}s
-
# (if there's interpolation in the selector).
-
# When the interpolation is resolved and the strings are joined together,
-
# this will be the string representation of this node.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
def to_a
-
Sass::Util.abstract(self)
-
end
-
-
# Returns a string representation of the node.
-
# This is basically the selector string.
-
#
-
# @return [String]
-
1
def inspect
-
to_a.map {|e| e.is_a?(Sass::Script::Node) ? "\#{#{e.to_sass}}" : e}.join
-
end
-
-
# @see \{#inspect}
-
# @return [String]
-
1
def to_s
-
inspect
-
end
-
-
# Returns a hash code for this selector object.
-
#
-
# By default, this is based on the value of \{#to\_a},
-
# so if that contains information irrelevant to the identity of the selector,
-
# this should be overridden.
-
#
-
# @return [Fixnum]
-
1
def hash
-
@_hash ||= to_a.hash
-
end
-
-
# Checks equality between this and another object.
-
#
-
# By default, this is based on the value of \{#to\_a},
-
# so if that contains information irrelevant to the identity of the selector,
-
# this should be overridden.
-
#
-
# @param other [Object] The object to test equality against
-
# @return [Boolean] Whether or not this is equal to `other`
-
1
def eql?(other)
-
other.class == self.class && other.hash == self.hash && other.to_a.eql?(to_a)
-
end
-
1
alias_method :==, :eql?
-
-
# Unifies this selector with a {SimpleSequence}'s {SimpleSequence#members members array},
-
# returning another `SimpleSequence` members array
-
# that matches both this selector and the input selector.
-
#
-
# By default, this just appends this selector to the end of the array
-
# (or returns the original array if this selector already exists in it).
-
#
-
# @param sels [Array<Simple>] A {SimpleSequence}'s {SimpleSequence#members members array}
-
# @return [Array<Simple>, nil] A {SimpleSequence} {SimpleSequence#members members array}
-
# matching both `sels` and this selector,
-
# or `nil` if this is impossible (e.g. unifying `#foo` and `#bar`)
-
# @raise [Sass::SyntaxError] If this selector cannot be unified.
-
# This will only ever occur when a dynamic selector,
-
# such as {Parent} or {Interpolation}, is used in unification.
-
# Since these selectors should be resolved
-
# by the time extension and unification happen,
-
# this exception will only ever be raised as a result of programmer error
-
1
def unify(sels)
-
return sels if sels.any? {|sel2| eql?(sel2)}
-
sels_with_ix = Sass::Util.enum_with_index(sels)
-
_, i =
-
if self.is_a?(Pseudo) || self.is_a?(SelectorPseudoClass)
-
sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) && (sels.last.final? || sels.last.type == :element)}
-
else
-
sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) || sel.is_a?(SelectorPseudoClass)}
-
end
-
return sels + [self] unless i
-
return sels[0...i] + [self] + sels[i..-1]
-
end
-
-
1
protected
-
-
# Unifies two namespaces,
-
# returning a namespace that works for both of them if possible.
-
#
-
# @param ns1 [String, nil] The first namespace.
-
# `nil` means none specified, e.g. `foo`.
-
# The empty string means no namespace specified, e.g. `|foo`.
-
# `"*"` means any namespace is allowed, e.g. `*|foo`.
-
# @param ns2 [String, nil] The second namespace. See `ns1`.
-
# @return [Array(String or nil, Boolean)]
-
# The first value is the unified namespace, or `nil` for no namespace.
-
# The second value is whether or not a namespace that works for both inputs
-
# could be found at all.
-
# If the second value is `false`, the first should be ignored.
-
1
def unify_namespaces(ns1, ns2)
-
return nil, false unless ns1 == ns2 || ns1.nil? || ns1 == ['*'] || ns2.nil? || ns2 == ['*']
-
return ns2, true if ns1 == ['*']
-
return ns1, true if ns2 == ['*']
-
return ns1 || ns2, true
-
end
-
end
-
end
-
end
-
1
module Sass
-
1
module Selector
-
# A unseparated sequence of selectors
-
# that all apply to a single element.
-
# For example, `.foo#bar[attr=baz]` is a simple sequence
-
# of the selectors `.foo`, `#bar`, and `[attr=baz]`.
-
1
class SimpleSequence < AbstractSequence
-
# The array of individual selectors.
-
#
-
# @return [Array<Simple>]
-
1
attr_reader :members
-
-
# Returns the element or universal selector in this sequence,
-
# if it exists.
-
#
-
# @return [Element, Universal, nil]
-
1
def base
-
@base ||= (members.first if members.first.is_a?(Element) || members.first.is_a?(Universal))
-
end
-
-
# Returns the non-base selectors in this sequence.
-
#
-
# @return [Set<Simple>]
-
1
def rest
-
@rest ||= Set.new(base ? members[1..-1] : members)
-
end
-
-
# @param selectors [Array<Simple>] See \{#members}
-
1
def initialize(selectors)
-
@members = selectors
-
end
-
-
# Resolves the {Parent} selectors within this selector
-
# by replacing them with the given parent selector,
-
# handling commas appropriately.
-
#
-
# @param super_seq [Sequence] The parent selector sequence
-
# @return [Array<SimpleSequence>] This selector, with parent references resolved.
-
# This is an array because the parent selector is itself a {Sequence}
-
# @raise [Sass::SyntaxError] If a parent selector is invalid
-
1
def resolve_parent_refs(super_seq)
-
# Parent selector only appears as the first selector in the sequence
-
return [self] unless @members.first.is_a?(Parent)
-
-
return super_seq.members if @members.size == 1
-
unless super_seq.members.last.is_a?(SimpleSequence)
-
raise Sass::SyntaxError.new("Invalid parent selector: " + super_seq.to_a.join)
-
end
-
-
super_seq.members[0...-1] +
-
[SimpleSequence.new(super_seq.members.last.members + @members[1..-1])]
-
end
-
-
# Non-destrucively extends this selector with the extensions specified in a hash
-
# (which should come from {Sass::Tree::Visitors::Cssize}).
-
#
-
# @overload def do_extend(extends)
-
# @param extends [{Selector::Simple => Selector::Sequence}]
-
# The extensions to perform on this selector
-
# @return [Array<Sequence>] A list of selectors generated
-
# by extending this selector with `extends`.
-
# @see CommaSequence#do_extend
-
1
def do_extend(extends, seen = Set.new)
-
extends.get(members.to_set).map do |seq, sels|
-
# If A {@extend B} and C {...},
-
# seq is A, sels is B, and self is C
-
-
self_without_sel = self.members - sels
-
next unless unified = seq.members.last.unify(self_without_sel)
-
[sels, seq.members[0...-1] + [unified]]
-
end.compact.map do |sels, seq|
-
seq = Sequence.new(seq)
-
seen.include?(sels) ? [] : seq.do_extend(extends, seen + [sels])
-
end.flatten.uniq
-
end
-
-
# Unifies this selector with another {SimpleSequence}'s {SimpleSequence#members members array},
-
# returning another `SimpleSequence`
-
# that matches both this selector and the input selector.
-
#
-
# @param sels [Array<Simple>] A {SimpleSequence}'s {SimpleSequence#members members array}
-
# @return [SimpleSequence, nil] A {SimpleSequence} matching both `sels` and this selector,
-
# or `nil` if this is impossible (e.g. unifying `#foo` and `#bar`)
-
# @raise [Sass::SyntaxError] If this selector cannot be unified.
-
# This will only ever occur when a dynamic selector,
-
# such as {Parent} or {Interpolation}, is used in unification.
-
# Since these selectors should be resolved
-
# by the time extension and unification happen,
-
# this exception will only ever be raised as a result of programmer error
-
1
def unify(sels)
-
return unless sseq = members.inject(sels) do |sseq, sel|
-
return unless sseq
-
sel.unify(sseq)
-
end
-
SimpleSequence.new(sseq)
-
end
-
-
# Returns whether or not this selector matches all elements
-
# that the given selector matches (as well as possibly more).
-
#
-
# @example
-
# (.foo).superselector?(.foo.bar) #=> true
-
# (.foo).superselector?(.bar) #=> false
-
# @param sseq [SimpleSequence]
-
# @return [Boolean]
-
1
def superselector?(sseq)
-
(base.nil? || base.eql?(sseq.base)) && rest.subset?(sseq.rest)
-
end
-
-
# @see Simple#to_a
-
1
def to_a
-
@members.map {|sel| sel.to_a}.flatten
-
end
-
-
# Returns a string representation of the sequence.
-
# This is basically the selector string.
-
#
-
# @return [String]
-
1
def inspect
-
members.map {|m| m.inspect}.join
-
end
-
-
1
private
-
-
1
def _hash
-
[base, Sass::Util.set_hash(rest)].hash
-
end
-
-
1
def _eql?(other)
-
other.base.eql?(self.base) && Sass::Util.set_eql?(other.rest, self.rest)
-
end
-
end
-
end
-
end
-
1
module Sass
-
# This module contains functionality that's shared between Haml and Sass.
-
1
module Shared
-
1
extend self
-
-
# Scans through a string looking for the interoplation-opening `#{`
-
# and, when it's found, yields the scanner to the calling code
-
# so it can handle it properly.
-
#
-
# The scanner will have any backslashes immediately in front of the `#{`
-
# as the second capture group (`scan[2]`),
-
# and the text prior to that as the first (`scan[1]`).
-
#
-
# @yieldparam scan [StringScanner] The scanner scanning through the string
-
# @return [String] The text remaining in the scanner after all `#{`s have been processed
-
1
def handle_interpolation(str)
-
scan = Sass::Util::MultibyteStringScanner.new(str)
-
yield scan while scan.scan(/(.*?)(\\*)\#\{/m)
-
scan.rest
-
end
-
-
# Moves a scanner through a balanced pair of characters.
-
# For example:
-
#
-
# Foo (Bar (Baz bang) bop) (Bang (bop bip))
-
# ^ ^
-
# from to
-
#
-
# @param scanner [StringScanner] The string scanner to move
-
# @param start [Character] The character opening the balanced pair.
-
# A `Fixnum` in 1.8, a `String` in 1.9
-
# @param finish [Character] The character closing the balanced pair.
-
# A `Fixnum` in 1.8, a `String` in 1.9
-
# @param count [Fixnum] The number of opening characters matched
-
# before calling this method
-
# @return [(String, String)] The string matched within the balanced pair
-
# and the rest of the string.
-
# `["Foo (Bar (Baz bang) bop)", " (Bang (bop bip))"]` in the example above.
-
1
def balance(scanner, start, finish, count = 0)
-
str = ''
-
scanner = Sass::Util::MultibyteStringScanner.new(scanner) unless scanner.is_a? StringScanner
-
regexp = Regexp.new("(.*?)[\\#{start.chr}\\#{finish.chr}]", Regexp::MULTILINE)
-
while scanner.scan(regexp)
-
str << scanner.matched
-
count += 1 if scanner.matched[-1] == start
-
count -= 1 if scanner.matched[-1] == finish
-
return [str.strip, scanner.rest] if count == 0
-
end
-
end
-
-
# Formats a string for use in error messages about indentation.
-
#
-
# @param indentation [String] The string used for indentation
-
# @param was [Boolean] Whether or not to add `"was"` or `"were"`
-
# (depending on how many characters were in `indentation`)
-
# @return [String] The name of the indentation (e.g. `"12 spaces"`, `"1 tab"`)
-
1
def human_indentation(indentation, was = false)
-
if !indentation.include?(?\t)
-
noun = 'space'
-
elsif !indentation.include?(?\s)
-
noun = 'tab'
-
else
-
return indentation.inspect + (was ? ' was' : '')
-
end
-
-
singular = indentation.length == 1
-
if was
-
was = singular ? ' was' : ' were'
-
else
-
was = ''
-
end
-
-
"#{indentation.length} #{noun}#{'s' unless singular}#{was}"
-
end
-
end
-
end
-
1
module Sass::Tree
-
# A static node representing an unproccessed Sass `@charset` directive.
-
#
-
# @see Sass::Tree
-
1
class CharsetNode < Node
-
# The name of the charset.
-
#
-
# @return [String]
-
1
attr_accessor :name
-
-
# @param name [String] see \{#name}
-
1
def initialize(name)
-
@name = name
-
super()
-
end
-
-
# @see Node#invisible?
-
1
def invisible?
-
!Sass::Util.ruby1_8?
-
end
-
end
-
end
-
1
require 'sass/tree/node'
-
-
1
module Sass::Tree
-
# A static node representing a Sass comment (silent or loud).
-
#
-
# @see Sass::Tree
-
1
class CommentNode < Node
-
# The text of the comment, not including `/*` and `*/`.
-
# Interspersed with {Sass::Script::Node}s representing `#{}`-interpolation
-
# if this is a loud comment.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_accessor :value
-
-
# The text of the comment
-
# after any interpolated SassScript has been resolved.
-
# Only set once \{Tree::Visitors::Perform} has been run.
-
#
-
# @return [String]
-
1
attr_accessor :resolved_value
-
-
# Whether the comment is loud.
-
#
-
# Loud comments start with ! and force the comment to be generated
-
# irrespective of compilation settings or the comment syntax used.
-
#
-
# @return [Boolean]
-
1
attr_accessor :loud
-
-
# Whether or not the comment is silent (that is, doesn't output to CSS).
-
#
-
# @return [Boolean]
-
1
attr_accessor :silent
-
-
# @param value [Array<String, Sass::Script::Node>] See \{#value}
-
# @param silent [Boolean] See \{#silent}
-
# @param loud [Boolean] See \{#loud}
-
1
def initialize(value, silent, loud)
-
@value = Sass::Util.with_extracted_values(value) {|str| normalize_indentation str}
-
@silent = silent
-
@loud = loud
-
super()
-
end
-
-
# Compares the contents of two comments.
-
#
-
# @param other [Object] The object to compare with
-
# @return [Boolean] Whether or not this node and the other object
-
# are the same
-
1
def ==(other)
-
self.class == other.class && value == other.value && silent == other.silent && loud == other.loud
-
end
-
-
# Returns `true` if this is a silent comment
-
# or the current style doesn't render comments.
-
#
-
# Comments starting with ! are never invisible (and the ! is removed from the output.)
-
#
-
# @return [Boolean]
-
1
def invisible?
-
if @loud
-
return false
-
else
-
@silent || (style == :compressed)
-
end
-
end
-
-
# Returns the number of lines in the comment.
-
#
-
# @return [Fixnum]
-
1
def lines
-
@value.inject(0) do |s, e|
-
next s + e.count("\n") if e.is_a?(String)
-
next s
-
end
-
end
-
-
1
private
-
-
1
def normalize_indentation(str)
-
pre = str.split("\n").inject(str[/^[ \t]*/].split("")) do |pre, line|
-
line[/^[ \t]*/].split("").zip(pre).inject([]) do |arr, (a, b)|
-
break arr if a != b
-
arr << a
-
end
-
end.join
-
str.gsub(/^#{pre}/, '')
-
end
-
end
-
end
-
1
module Sass
-
1
module Tree
-
# A dynamic node representing a Sass `@debug` statement.
-
#
-
# @see Sass::Tree
-
1
class DebugNode < Node
-
# The expression to print.
-
# @return [Script::Node]
-
1
attr_accessor :expr
-
-
# @param expr [Script::Node] The expression to print
-
1
def initialize(expr)
-
@expr = expr
-
super()
-
end
-
end
-
end
-
end
-
1
module Sass::Tree
-
# A static node representing an unproccessed Sass `@`-directive.
-
# Directives known to Sass, like `@for` and `@debug`,
-
# are handled by their own nodes;
-
# only CSS directives like `@media` and `@font-face` become {DirectiveNode}s.
-
#
-
# `@import` and `@charset` are special cases;
-
# they become {ImportNode}s and {CharsetNode}s, respectively.
-
#
-
# @see Sass::Tree
-
1
class DirectiveNode < Node
-
# The text of the directive, `@` and all.
-
#
-
# @return [String]
-
1
attr_accessor :value
-
-
# @param value [String] See \{#value}
-
1
def initialize(value)
-
@value = value
-
super()
-
end
-
end
-
end
-
1
require 'sass/tree/node'
-
-
1
module Sass::Tree
-
# A dynamic node representing a Sass `@each` loop.
-
#
-
# @see Sass::Tree
-
1
class EachNode < Node
-
# The name of the loop variable.
-
# @return [String]
-
1
attr_reader :var
-
-
# The parse tree for the list.
-
# @param [Script::Node]
-
1
attr_accessor :list
-
-
# @param var [String] The name of the loop variable
-
# @param list [Script::Node] The parse tree for the list
-
1
def initialize(var, list)
-
@var = var
-
@list = list
-
super()
-
end
-
end
-
end
-
1
require 'sass/tree/node'
-
-
1
module Sass::Tree
-
# A static node reprenting an `@extend` directive.
-
#
-
# @see Sass::Tree
-
1
class ExtendNode < Node
-
# The parsed selector after interpolation has been resolved.
-
# Only set once {Tree::Visitors::Perform} has been run.
-
#
-
# @return [Selector::CommaSequence]
-
1
attr_accessor :resolved_selector
-
-
# The CSS selector to extend, interspersed with {Sass::Script::Node}s
-
# representing `#{}`-interpolation.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_accessor :selector
-
-
# @param selector [Array<String, Sass::Script::Node>]
-
# The CSS selector to extend,
-
# interspersed with {Sass::Script::Node}s
-
# representing `#{}`-interpolation.
-
1
def initialize(selector)
-
@selector = selector
-
super()
-
end
-
end
-
end
-
1
require 'sass/tree/node'
-
-
1
module Sass::Tree
-
# A dynamic node representing a Sass `@for` loop.
-
#
-
# @see Sass::Tree
-
1
class ForNode < Node
-
# The name of the loop variable.
-
# @return [String]
-
1
attr_reader :var
-
-
# The parse tree for the initial expression.
-
# @return [Script::Node]
-
1
attr_accessor :from
-
-
# The parse tree for the final expression.
-
# @return [Script::Node]
-
1
attr_accessor :to
-
-
# Whether to include `to` in the loop or stop just before.
-
# @return [Boolean]
-
1
attr_reader :exclusive
-
-
# @param var [String] See \{#var}
-
# @param from [Script::Node] See \{#from}
-
# @param to [Script::Node] See \{#to}
-
# @param exclusive [Boolean] See \{#exclusive}
-
1
def initialize(var, from, to, exclusive)
-
@var = var
-
@from = from
-
@to = to
-
@exclusive = exclusive
-
super()
-
end
-
end
-
end
-
1
module Sass
-
1
module Tree
-
# A dynamic node representing a function definition.
-
#
-
# @see Sass::Tree
-
1
class FunctionNode < Node
-
# The name of the function.
-
# @return [String]
-
1
attr_reader :name
-
-
# The arguments to the function. Each element is a tuple
-
# containing the variable for argument and the parse tree for
-
# the default value of the argument
-
#
-
# @return [Array<Script::Node>]
-
1
attr_accessor :args
-
-
# @param name [String] The function name
-
# @param args [Array<(Script::Node, Script::Node)>] The arguments for the function.
-
1
def initialize(name, args)
-
@name = name
-
@args = args
-
super()
-
end
-
end
-
end
-
end
-
1
require 'sass/tree/node'
-
-
1
module Sass::Tree
-
# A dynamic node representing a Sass `@if` statement.
-
#
-
# {IfNode}s are a little odd, in that they also represent `@else` and `@else if`s.
-
# This is done as a linked list:
-
# each {IfNode} has a link (\{#else}) to the next {IfNode}.
-
#
-
# @see Sass::Tree
-
1
class IfNode < Node
-
# The conditional expression.
-
# If this is nil, this is an `@else` node, not an `@else if`.
-
#
-
# @return [Script::Expr]
-
1
attr_accessor :expr
-
-
# The next {IfNode} in the if-else list, or `nil`.
-
#
-
# @return [IfNode]
-
1
attr_accessor :else
-
-
# @param expr [Script::Expr] See \{#expr}
-
1
def initialize(expr)
-
@expr = expr
-
@last_else = self
-
super()
-
end
-
-
# Append an `@else` node to the end of the list.
-
#
-
# @param node [IfNode] The `@else` node to append
-
1
def add_else(node)
-
@last_else.else = node
-
@last_else = node
-
end
-
-
1
def _dump(f)
-
Marshal.dump([self.expr, self.else, self.children])
-
end
-
-
1
def self._load(data)
-
expr, else_, children = Marshal.load(data)
-
node = IfNode.new(expr)
-
node.else = else_
-
node.children = children
-
node.instance_variable_set('@last_else',
-
node.else ? node.else.instance_variable_get('@last_else') : node)
-
node
-
end
-
end
-
end
-
1
module Sass
-
1
module Tree
-
# A static node that wraps the {Sass::Tree} for an `@import`ed file.
-
# It doesn't have a functional purpose other than to add the `@import`ed file
-
# to the backtrace if an error occurs.
-
1
class ImportNode < RootNode
-
# The name of the imported file as it appears in the Sass document.
-
#
-
# @return [String]
-
1
attr_reader :imported_filename
-
-
# @param imported_filename [String] The name of the imported file
-
1
def initialize(imported_filename)
-
@imported_filename = imported_filename
-
super(nil)
-
end
-
-
1
def invisible?; to_s.empty?; end
-
-
# Returns the imported file.
-
#
-
# @return [Sass::Engine]
-
# @raise [Sass::SyntaxError] If no file could be found to import.
-
1
def imported_file
-
@imported_file ||= import
-
end
-
-
# Returns whether or not this import should emit a CSS @import declaration
-
#
-
# @return [Boolean] Whether or not this is a simple CSS @import declaration.
-
1
def css_import?
-
if @imported_filename =~ /\.css$/
-
@imported_filename
-
elsif imported_file.is_a?(String) && imported_file =~ /\.css$/
-
imported_file
-
end
-
end
-
-
1
private
-
-
1
def import
-
paths = @options[:load_paths]
-
-
if @options[:importer]
-
f = @options[:importer].find_relative(
-
@imported_filename, @options[:filename], @options.dup)
-
return f if f
-
end
-
-
paths.each do |p|
-
if f = p.find(@imported_filename, @options.dup)
-
return f
-
end
-
end
-
-
message = "File to import not found or unreadable: #{@imported_filename}.\n"
-
if paths.size == 1
-
message << "Load path: #{paths.first}"
-
else
-
message << "Load paths:\n " << paths.join("\n ")
-
end
-
raise SyntaxError.new(message)
-
rescue SyntaxError => e
-
raise SyntaxError.new(e.message, :line => self.line, :filename => @filename)
-
end
-
end
-
end
-
end
-
1
module Sass::Tree
-
# A static node representing a `@media` rule.
-
# `@media` rules behave differently from other directives
-
# in that when they're nested within rules,
-
# they bubble up to top-level.
-
#
-
# @see Sass::Tree
-
1
class MediaNode < DirectiveNode
-
# The media query. A list of comma-separated queries (e.g. `print` or `screen`).
-
#
-
# @return [Array<String>]
-
1
attr_accessor :query
-
-
# @see RuleNode#tabs
-
1
attr_accessor :tabs
-
-
# @see RuleNode#group_end
-
1
attr_accessor :group_end
-
-
# @param query [Array<String>] See \{#query}
-
1
def initialize(query)
-
@query = query
-
@tabs = 0
-
super('')
-
end
-
-
# @see DirectiveNode#value
-
1
def value
-
"@media #{query.join(', ')}"
-
end
-
end
-
end
-
1
module Sass
-
1
module Tree
-
# A dynamic node representing a mixin definition.
-
#
-
# @see Sass::Tree
-
1
class MixinDefNode < Node
-
# The mixin name.
-
# @return [String]
-
1
attr_reader :name
-
-
# The arguments for the mixin.
-
# Each element is a tuple containing the variable for argument
-
# and the parse tree for the default value of the argument.
-
#
-
# @return [Array<(Script::Node, Script::Node)>]
-
1
attr_accessor :args
-
-
# @param name [String] The mixin name
-
# @param args [Array<(Script::Node, Script::Node)>] See \{#args}
-
1
def initialize(name, args)
-
@name = name
-
@args = args
-
super()
-
end
-
end
-
end
-
end
-
1
require 'sass/tree/node'
-
-
1
module Sass::Tree
-
# A static node representing a mixin include.
-
# When in a static tree, the sole purpose is to wrap exceptions
-
# to add the mixin to the backtrace.
-
#
-
# @see Sass::Tree
-
1
class MixinNode < Node
-
# The name of the mixin.
-
# @return [String]
-
1
attr_reader :name
-
-
# The arguments to the mixin.
-
# @return [Array<Script::Node>]
-
1
attr_accessor :args
-
-
# A hash from keyword argument names to values.
-
# @return [{String => Script::Node}]
-
1
attr_accessor :keywords
-
-
# @param name [String] The name of the mixin
-
# @param args [Array<Script::Node>] See \{#args}
-
# @param keywords [{String => Script::Node}] See \{#keywords}
-
1
def initialize(name, args, keywords)
-
@name = name
-
@args = args
-
@keywords = keywords
-
super()
-
end
-
end
-
end
-
1
module Sass
-
# A namespace for nodes in the Sass parse tree.
-
#
-
# The Sass parse tree has three states: dynamic, static Sass, and static CSS.
-
#
-
# When it's first parsed, a Sass document is in the dynamic state.
-
# It has nodes for mixin definitions and `@for` loops and so forth,
-
# in addition to nodes for CSS rules and properties.
-
# Nodes that only appear in this state are called **dynamic nodes**.
-
#
-
# {Tree::Visitors::Perform} creates a static Sass tree, which is different.
-
# It still has nodes for CSS rules and properties
-
# but it doesn't have any dynamic-generation-related nodes.
-
# The nodes in this state are in the same structure as the Sass document:
-
# rules and properties are nested beneath one another.
-
# Nodes that can be in this state or in the dynamic state
-
# are called **static nodes**.
-
#
-
# {Tree::Visitors::Cssize} is then used to create a static CSS tree.
-
# This is like a static Sass tree,
-
# but the structure exactly mirrors that of the generated CSS.
-
# Rules and properties can't be nested beneath one another in this state.
-
#
-
# Finally, {Tree::Visitors::ToCss} can be called on a static CSS tree
-
# to get the actual CSS code as a string.
-
1
module Tree
-
# The abstract superclass of all parse-tree nodes.
-
1
class Node
-
1
include Enumerable
-
-
# The child nodes of this node.
-
#
-
# @return [Array<Tree::Node>]
-
1
attr_accessor :children
-
-
# Whether or not this node has child nodes.
-
# This may be true even when \{#children} is empty,
-
# in which case this node has an empty block (e.g. `{}`).
-
#
-
# @return [Boolean]
-
1
attr_accessor :has_children
-
-
# The line of the document on which this node appeared.
-
#
-
# @return [Fixnum]
-
1
attr_accessor :line
-
-
# The name of the document on which this node appeared.
-
#
-
# @return [String]
-
1
attr_writer :filename
-
-
# The options hash for the node.
-
# See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
-
#
-
# @return [{Symbol => Object}]
-
1
attr_reader :options
-
-
1
def initialize
-
@children = []
-
end
-
-
# Sets the options hash for the node and all its children.
-
#
-
# @param options [{Symbol => Object}] The options
-
# @see #options
-
1
def options=(options)
-
Sass::Tree::Visitors::SetOptions.visit(self, options)
-
end
-
-
# @private
-
1
def children=(children)
-
self.has_children ||= !children.empty?
-
@children = children
-
end
-
-
# The name of the document on which this node appeared.
-
#
-
# @return [String]
-
1
def filename
-
@filename || (@options && @options[:filename])
-
end
-
-
# Appends a child to the node.
-
#
-
# @param child [Tree::Node, Array<Tree::Node>] The child node or nodes
-
# @raise [Sass::SyntaxError] if `child` is invalid
-
1
def <<(child)
-
return if child.nil?
-
if child.is_a?(Array)
-
child.each {|c| self << c}
-
else
-
self.has_children = true
-
@children << child
-
end
-
end
-
-
# Compares this node and another object (only other {Tree::Node}s will be equal).
-
# This does a structural comparison;
-
# if the contents of the nodes and all the child nodes are equivalent,
-
# then the nodes are as well.
-
#
-
# Only static nodes need to override this.
-
#
-
# @param other [Object] The object to compare with
-
# @return [Boolean] Whether or not this node and the other object
-
# are the same
-
# @see Sass::Tree
-
1
def ==(other)
-
self.class == other.class && other.children == children
-
end
-
-
# True if \{#to\_s} will return `nil`;
-
# that is, if the node shouldn't be rendered.
-
# Should only be called in a static tree.
-
#
-
# @return [Boolean]
-
1
def invisible?; false; end
-
-
# The output style. See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
-
#
-
# @return [Symbol]
-
1
def style
-
@options[:style]
-
end
-
-
# Computes the CSS corresponding to this static CSS tree.
-
#
-
# @return [String, nil] The resulting CSS
-
# @see Sass::Tree
-
1
def to_s
-
Sass::Tree::Visitors::ToCss.visit(self)
-
end
-
-
# Converts a static CSS tree (e.g. the output of \{Tree::Visitors::Cssize})
-
# into another static CSS tree,
-
# with the given extensions applied to all relevant {RuleNode}s.
-
#
-
# @todo Link this to the reference documentation on `@extend`
-
# when such a thing exists.
-
#
-
# @param extends [Sass::Util::SubsetMap{Selector::Simple => Selector::Sequence}]
-
# The extensions to perform on this tree
-
# @return [Tree::Node] The resulting tree of static CSS nodes.
-
# @raise [Sass::SyntaxError] Only if there's a programmer error
-
# and this is not a static CSS tree
-
1
def do_extend(extends)
-
node = dup
-
node.children = children.map {|c| c.do_extend(extends)}
-
node
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace(:filename => filename, :line => line)
-
raise e
-
end
-
-
# Iterates through each node in the tree rooted at this node
-
# in a pre-order walk.
-
#
-
# @yield node
-
# @yieldparam node [Node] a node in the tree
-
1
def each
-
yield self
-
children.each {|c| c.each {|n| yield n}}
-
end
-
-
# Converts a node to Sass code that will generate it.
-
#
-
# @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
-
# @return [String] The Sass code corresponding to the node
-
1
def to_sass(options = {})
-
Sass::Tree::Visitors::Convert.visit(self, options, :sass)
-
end
-
-
# Converts a node to SCSS code that will generate it.
-
#
-
# @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
-
# @return [String] The Sass code corresponding to the node
-
1
def to_scss(options = {})
-
Sass::Tree::Visitors::Convert.visit(self, options, :scss)
-
end
-
-
# Return a deep clone of this node.
-
# The child nodes are cloned, but options are not.
-
#
-
# @return [Node]
-
1
def deep_copy
-
Sass::Tree::Visitors::DeepCopy.visit(self)
-
end
-
-
1
protected
-
-
# @see Sass::Shared.balance
-
# @raise [Sass::SyntaxError] if the brackets aren't balanced
-
1
def balance(*args)
-
res = Sass::Shared.balance(*args)
-
return res if res
-
raise Sass::SyntaxError.new("Unbalanced brackets.", :line => line)
-
end
-
end
-
end
-
end
-
1
module Sass::Tree
-
# A static node reprenting a CSS property.
-
#
-
# @see Sass::Tree
-
1
class PropNode < Node
-
# The name of the property,
-
# interspersed with {Sass::Script::Node}s
-
# representing `#{}`-interpolation.
-
# Any adjacent strings will be merged together.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_accessor :name
-
-
# The name of the property
-
# after any interpolated SassScript has been resolved.
-
# Only set once \{Tree::Visitors::Perform} has been run.
-
#
-
# @return [String]
-
1
attr_accessor :resolved_name
-
-
# The value of the property.
-
#
-
# @return [Sass::Script::Node]
-
1
attr_accessor :value
-
-
# The value of the property
-
# after any interpolated SassScript has been resolved.
-
# Only set once \{Tree::Visitors::Perform} has been run.
-
#
-
# @return [String]
-
1
attr_accessor :resolved_value
-
-
# How deep this property is indented
-
# relative to a normal property.
-
# This is only greater than 0 in the case that:
-
#
-
# * This node is in a CSS tree
-
# * The style is :nested
-
# * This is a child property of another property
-
# * The parent property has a value, and thus will be rendered
-
#
-
# @return [Fixnum]
-
1
attr_accessor :tabs
-
-
# @param name [Array<String, Sass::Script::Node>] See \{#name}
-
# @param value [Sass::Script::Node] See \{#value}
-
# @param prop_syntax [Symbol] `:new` if this property uses `a: b`-style syntax,
-
# `:old` if it uses `:a b`-style syntax
-
1
def initialize(name, value, prop_syntax)
-
@name = Sass::Util.strip_string_array(
-
Sass::Util.merge_adjacent_strings(name))
-
@value = value
-
@tabs = 0
-
@prop_syntax = prop_syntax
-
super()
-
end
-
-
# Compares the names and values of two properties.
-
#
-
# @param other [Object] The object to compare with
-
# @return [Boolean] Whether or not this node and the other object
-
# are the same
-
1
def ==(other)
-
self.class == other.class && name == other.name && value == other.value && super
-
end
-
-
# Returns a appropriate message indicating how to escape pseudo-class selectors.
-
# This only applies for old-style properties with no value,
-
# so returns the empty string if this is new-style.
-
#
-
# @return [String] The message
-
1
def pseudo_class_selector_message
-
return "" if @prop_syntax == :new || !value.is_a?(Sass::Script::String) || !value.value.empty?
-
"\nIf #{declaration.dump} should be a selector, use \"\\#{declaration}\" instead."
-
end
-
-
# Computes the Sass or SCSS code for the variable declaration.
-
# This is like \{#to\_scss} or \{#to\_sass},
-
# except it doesn't print any child properties or a trailing semicolon.
-
#
-
# @param opts [{Symbol => Object}] The options hash for the tree.
-
# @param fmt [Symbol] `:scss` or `:sass`.
-
1
def declaration(opts = {:old => @prop_syntax == :old}, fmt = :sass)
-
name = self.name.map {|n| n.is_a?(String) ? n : "\#{#{n.to_sass(opts)}}"}.join
-
if name[0] == ?:
-
raise Sass::SyntaxError.new("The \"#{name}: #{self.class.val_to_sass(value, opts)}\" hack is not allowed in the Sass indented syntax")
-
end
-
-
old = opts[:old] && fmt == :sass
-
initial = old ? ':' : ''
-
mid = old ? '' : ':'
-
"#{initial}#{name}#{mid} #{self.class.val_to_sass(value, opts)}".rstrip
-
end
-
-
1
private
-
-
1
def check!
-
if @options[:property_syntax] && @options[:property_syntax] != @prop_syntax
-
raise Sass::SyntaxError.new(
-
"Illegal property syntax: can't use #{@prop_syntax} syntax when :property_syntax => #{@options[:property_syntax].inspect} is set.")
-
elsif resolved_value.empty?
-
raise Sass::SyntaxError.new("Invalid property: #{declaration.dump} (no value)." +
-
pseudo_class_selector_message)
-
end
-
end
-
-
1
class << self
-
# @private
-
1
def val_to_sass(value, opts)
-
val_to_sass_comma(value, opts).to_sass(opts)
-
end
-
-
1
private
-
-
1
def val_to_sass_comma(node, opts)
-
return node unless node.is_a?(Sass::Script::Operation)
-
return val_to_sass_concat(node, opts) unless node.operator == :comma
-
-
Sass::Script::Operation.new(
-
val_to_sass_concat(node.operand1, opts),
-
val_to_sass_comma(node.operand2, opts),
-
node.operator)
-
end
-
-
1
def val_to_sass_concat(node, opts)
-
return node unless node.is_a?(Sass::Script::Operation)
-
return val_to_sass_div(node, opts) unless node.operator == :space
-
-
Sass::Script::Operation.new(
-
val_to_sass_div(node.operand1, opts),
-
val_to_sass_concat(node.operand2, opts),
-
node.operator)
-
end
-
-
1
def val_to_sass_div(node, opts)
-
unless node.is_a?(Sass::Script::Operation) && node.operator == :div &&
-
node.operand1.is_a?(Sass::Script::Number) &&
-
node.operand2.is_a?(Sass::Script::Number) &&
-
(!node.operand1.original || !node.operand2.original)
-
return node
-
end
-
-
Sass::Script::String.new("(#{node.to_sass(opts)})")
-
end
-
-
end
-
end
-
end
-
1
module Sass
-
1
module Tree
-
# A dynamic node representing returning from a function.
-
#
-
# @see Sass::Tree
-
1
class ReturnNode < Node
-
# The expression to return.
-
# @type [Script::Node]
-
1
attr_accessor :expr
-
-
# @param expr [Script::Node] The expression to return
-
1
def initialize(expr)
-
@expr = expr
-
super()
-
end
-
end
-
end
-
end
-
1
module Sass
-
1
module Tree
-
# A static node that is the root node of the Sass document.
-
1
class RootNode < Node
-
# The Sass template from which this node was created
-
#
-
# @param template [String]
-
1
attr_reader :template
-
-
# @param template [String] The Sass template from which this node was created
-
1
def initialize(template)
-
super()
-
@template = template
-
end
-
-
# Runs the dynamic Sass code *and* computes the CSS for the tree.
-
# @see #to_s
-
1
def render
-
Visitors::CheckNesting.visit(self)
-
result = Visitors::Perform.visit(self)
-
Visitors::CheckNesting.visit(result) # Check again to validate mixins
-
result, extends = Visitors::Cssize.visit(result)
-
result = result.do_extend(extends) unless extends.empty?
-
result.to_s
-
end
-
end
-
end
-
end
-
1
require 'pathname'
-
1
require 'uri'
-
-
1
module Sass::Tree
-
# A static node reprenting a CSS rule.
-
#
-
# @see Sass::Tree
-
1
class RuleNode < Node
-
# The character used to include the parent selector
-
1
PARENT = '&'
-
-
# The CSS selector for this rule,
-
# interspersed with {Sass::Script::Node}s
-
# representing `#{}`-interpolation.
-
# Any adjacent strings will be merged together.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_accessor :rule
-
-
# The CSS selector for this rule,
-
# without any unresolved interpolation
-
# but with parent references still intact.
-
# It's only set once {Tree::Node#perform} has been called.
-
#
-
# @return [Selector::CommaSequence]
-
1
attr_accessor :parsed_rules
-
-
# The CSS selector for this rule,
-
# without any unresolved interpolation or parent references.
-
# It's only set once {Tree::Visitors::Cssize} has been run.
-
#
-
# @return [Selector::CommaSequence]
-
1
attr_accessor :resolved_rules
-
-
# How deep this rule is indented
-
# relative to a base-level rule.
-
# This is only greater than 0 in the case that:
-
#
-
# * This node is in a CSS tree
-
# * The style is :nested
-
# * This is a child rule of another rule
-
# * The parent rule has properties, and thus will be rendered
-
#
-
# @return [Fixnum]
-
1
attr_accessor :tabs
-
-
# Whether or not this rule is the last rule in a nested group.
-
# This is only set in a CSS tree.
-
#
-
# @return [Boolean]
-
1
attr_accessor :group_end
-
-
# The stack trace.
-
# This is only readable in a CSS tree as it is written during the perform step
-
# and only when the :trace_selectors option is set.
-
#
-
# @return [Array<String>]
-
1
attr_accessor :stack_trace
-
-
# @param rule [Array<String, Sass::Script::Node>]
-
# The CSS rule. See \{#rule}
-
1
def initialize(rule)
-
merged = Sass::Util.merge_adjacent_strings(rule)
-
@rule = Sass::Util.strip_string_array(merged)
-
@tabs = 0
-
try_to_parse_non_interpolated_rules
-
super()
-
end
-
-
# If we've precached the parsed selector, set the line on it, too.
-
1
def line=(line)
-
@parsed_rules.line = line if @parsed_rules
-
super
-
end
-
-
# If we've precached the parsed selector, set the filename on it, too.
-
1
def filename=(filename)
-
@parsed_rules.filename = filename if @parsed_rules
-
super
-
end
-
-
# Compares the contents of two rules.
-
#
-
# @param other [Object] The object to compare with
-
# @return [Boolean] Whether or not this node and the other object
-
# are the same
-
1
def ==(other)
-
self.class == other.class && rule == other.rule && super
-
end
-
-
# Adds another {RuleNode}'s rules to this one's.
-
#
-
# @param node [RuleNode] The other node
-
1
def add_rules(node)
-
@rule = Sass::Util.strip_string_array(
-
Sass::Util.merge_adjacent_strings(@rule + ["\n"] + node.rule))
-
try_to_parse_non_interpolated_rules
-
end
-
-
# @return [Boolean] Whether or not this rule is continued on the next line
-
1
def continued?
-
last = @rule.last
-
last.is_a?(String) && last[-1] == ?,
-
end
-
-
# Extends this Rule's selector with the given `extends`.
-
#
-
# @see Node#do_extend
-
1
def do_extend(extends)
-
node = dup
-
node.resolved_rules = resolved_rules.do_extend(extends)
-
node
-
end
-
-
# A hash that will be associated with this rule in the CSS document
-
# if the {file:SASS_REFERENCE.md#debug_info-option `:debug_info` option} is enabled.
-
# This data is used by e.g. [the FireSass Firebug extension](https://addons.mozilla.org/en-US/firefox/addon/103988).
-
#
-
# @return [{#to_s => #to_s}]
-
1
def debug_info
-
{:filename => filename && ("file://" + URI.escape(File.expand_path(filename))),
-
:line => self.line}
-
end
-
-
1
private
-
-
1
def try_to_parse_non_interpolated_rules
-
if @rule.all? {|t| t.kind_of?(String)}
-
# We don't use real filename/line info because we don't have it yet.
-
# When we get it, we'll set it on the parsed rules if possible.
-
parser = Sass::SCSS::StaticParser.new(@rule.join.strip, '', 1)
-
@parsed_rules = parser.parse_selector rescue nil
-
end
-
end
-
end
-
end
-
1
module Sass
-
1
module Tree
-
# A dynamic node representing a variable definition.
-
#
-
# @see Sass::Tree
-
1
class VariableNode < Node
-
# The name of the variable.
-
# @return [String]
-
1
attr_reader :name
-
-
# The parse tree for the variable value.
-
# @return [Script::Node]
-
1
attr_accessor :expr
-
-
# Whether this is a guarded variable assignment (`!default`).
-
# @return [Boolean]
-
1
attr_reader :guarded
-
-
# @param name [String] The name of the variable
-
# @param expr [Script::Node] See \{#expr}
-
# @param guarded [Boolean] See \{#guarded}
-
1
def initialize(name, expr, guarded)
-
@name = name
-
@expr = expr
-
@guarded = guarded
-
super()
-
end
-
end
-
end
-
end
-
# Visitors are used to traverse the Sass parse tree.
-
# Visitors should extend {Visitors::Base},
-
# which provides a small amount of scaffolding for traversal.
-
1
module Sass::Tree::Visitors
-
# The abstract base class for Sass visitors.
-
# Visitors should extend this class,
-
# then implement `visit_*` methods for each node they care about
-
# (e.g. `visit_rule` for {RuleNode} or `visit_for` for {ForNode}).
-
# These methods take the node in question as argument.
-
# They may `yield` to visit the child nodes of the current node.
-
#
-
# *Note*: due to the unusual nature of {Sass::Tree::IfNode},
-
# special care must be taken to ensure that it is properly handled.
-
# In particular, there is no built-in scaffolding
-
# for dealing with the return value of `@else` nodes.
-
#
-
# @abstract
-
1
class Base
-
# Runs the visitor on a tree.
-
#
-
# @param root [Tree::Node] The root node of the Sass tree.
-
# @return [Object] The return value of \{#visit} for the root node.
-
1
def self.visit(root)
-
new.send(:visit, root)
-
end
-
-
1
protected
-
-
# Runs the visitor on the given node.
-
# This can be overridden by subclasses that need to do something for each node.
-
#
-
# @param node [Tree::Node] The node to visit.
-
# @return [Object] The return value of the `visit_*` method for this node.
-
1
def visit(node)
-
method = "visit_#{node_name node}"
-
if self.respond_to?(method)
-
self.send(method, node) {visit_children(node)}
-
else
-
visit_children(node)
-
end
-
end
-
-
# Visit the child nodes for a given node.
-
# This can be overridden by subclasses that need to do something
-
# with the child nodes' return values.
-
#
-
# This method is run when `visit_*` methods `yield`,
-
# and its return value is returned from the `yield`.
-
#
-
# @param parent [Tree::Node] The parent node of the children to visit.
-
# @return [Array<Object>] The return values of the `visit_*` methods for the children.
-
1
def visit_children(parent)
-
parent.children.map {|c| visit(c)}
-
end
-
-
1
NODE_NAME_RE = /.*::(.*?)Node$/
-
-
# Returns the name of a node as used in the `visit_*` method.
-
#
-
# @param [Tree::Node] node The node.
-
# @return [String] The name.
-
1
def node_name(node)
-
@@node_names ||= {}
-
@@node_names[node.class.name] ||= node.class.name.gsub(NODE_NAME_RE, '\\1').downcase
-
end
-
-
# `yield`s, then runs the visitor on the `@else` clause if the node has one.
-
# This exists to ensure that the contents of the `@else` clause get visited.
-
1
def visit_if(node)
-
yield
-
visit(node.else) if node.else
-
node
-
end
-
end
-
end
-
# A visitor for checking that all nodes are properly nested.
-
1
class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
-
1
protected
-
-
1
def visit(node)
-
if error = (@parent && (
-
try_send("invalid_#{node_name @parent}_child?", @parent, node) ||
-
try_send("invalid_#{node_name node}_parent?", @parent, node))) ||
-
(@real_parent && (
-
try_send("invalid_#{node_name @real_parent}_real_child?", @real_parent, node) ||
-
try_send("invalid_#{node_name node}_real_parent?", @real_parent, node)))
-
raise Sass::SyntaxError.new(error)
-
end
-
super
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace(:filename => node.filename, :line => node.line)
-
raise e
-
end
-
-
1
CONTROL_NODES = [Sass::Tree::EachNode, Sass::Tree::ForNode, Sass::Tree::IfNode, Sass::Tree::WhileNode]
-
1
SCRIPT_NODES = [Sass::Tree::ImportNode, Sass::Tree::MixinNode] + CONTROL_NODES
-
1
def visit_children(parent)
-
old_parent = @parent
-
@parent = parent unless is_any_of?(parent, SCRIPT_NODES)
-
old_real_parent, @real_parent = @real_parent, parent
-
super
-
ensure
-
@parent = old_parent
-
@real_parent = old_real_parent
-
end
-
-
1
def visit_root(node)
-
yield
-
rescue Sass::SyntaxError => e
-
e.sass_template ||= node.template
-
raise e
-
end
-
-
1
def visit_import(node)
-
yield
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace(:filename => node.children.first.filename)
-
e.add_backtrace(:filename => node.filename, :line => node.line)
-
raise e
-
end
-
-
1
def invalid_charset_parent?(parent, child)
-
"@charset may only be used at the root of a document." unless parent.is_a?(Sass::Tree::RootNode)
-
end
-
-
1
VALID_EXTEND_PARENTS = [Sass::Tree::RuleNode, Sass::Tree::MixinDefNode]
-
1
def invalid_extend_parent?(parent, child)
-
unless is_any_of?(parent, VALID_EXTEND_PARENTS)
-
"Extend directives may only be used within rules."
-
end
-
end
-
-
1
def invalid_function_parent?(parent, child)
-
"Functions may only be defined at the root of a document." unless parent.is_a?(Sass::Tree::RootNode)
-
end
-
-
1
VALID_FUNCTION_CHILDREN = [
-
Sass::Tree::CommentNode, Sass::Tree::DebugNode, Sass::Tree::ReturnNode,
-
Sass::Tree::VariableNode, Sass::Tree::WarnNode
-
] + CONTROL_NODES
-
1
def invalid_function_child?(parent, child)
-
unless is_any_of?(child, VALID_FUNCTION_CHILDREN)
-
"Functions can only contain variable declarations and control directives."
-
end
-
end
-
-
1
VALID_IMPORT_PARENTS = [
-
Sass::Tree::IfNode, Sass::Tree::ForNode, Sass::Tree::WhileNode,
-
Sass::Tree::EachNode, Sass::Tree::MixinDefNode
-
]
-
1
def invalid_import_parent?(parent, child)
-
if is_any_of?(@real_parent, VALID_IMPORT_PARENTS)
-
return "Import directives may not be used within control directives or mixins."
-
end
-
return if parent.is_a?(Sass::Tree::RootNode)
-
return "CSS import directives may only be used at the root of a document." if child.css_import?
-
# If this is a nested @import, we need to make sure it doesn't have anything
-
# that's legal at top-level but not in the current context (e.g. mixin defs).
-
child.imported_file.to_tree.children.each {|c| visit(c)}
-
nil
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace(:filename => child.imported_file.options[:filename])
-
e.add_backtrace(:filename => child.filename, :line => child.line)
-
raise e
-
end
-
-
1
def invalid_import_real_parent?(parent, child)
-
-
end
-
-
1
def invalid_mixindef_parent?(parent, child)
-
"Mixins may only be defined at the root of a document." unless parent.is_a?(Sass::Tree::RootNode)
-
end
-
-
1
VALID_PROP_CHILDREN = [Sass::Tree::CommentNode, Sass::Tree::PropNode, Sass::Tree::MixinNode] + CONTROL_NODES
-
1
def invalid_prop_child?(parent, child)
-
unless is_any_of?(child, VALID_PROP_CHILDREN)
-
"Illegal nesting: Only properties may be nested beneath properties."
-
end
-
end
-
-
1
VALID_PROP_PARENTS = [Sass::Tree::RuleNode, Sass::Tree::PropNode,
-
Sass::Tree::MixinDefNode, Sass::Tree::DirectiveNode]
-
1
def invalid_prop_parent?(parent, child)
-
unless is_any_of?(parent, VALID_PROP_PARENTS)
-
"Properties are only allowed within rules, directives, or other properties." + child.pseudo_class_selector_message
-
end
-
end
-
-
1
def invalid_return_parent?(parent, child)
-
"@return may only be used within a function." unless parent.is_a?(Sass::Tree::FunctionNode)
-
end
-
-
1
private
-
-
1
def is_any_of?(val, classes)
-
for c in classes
-
return true if val.is_a?(c)
-
end
-
return false
-
end
-
-
1
def try_send(method, *args)
-
return unless respond_to?(method)
-
send(method, *args)
-
end
-
end
-
-
# A visitor for converting a Sass tree into a source string.
-
1
class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
-
# Runs the visitor on a tree.
-
#
-
# @param root [Tree::Node] The root node of the Sass tree.
-
# @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
-
# @param format [Symbol] `:sass` or `:scss`.
-
# @return [String] The Sass or SCSS source for the tree.
-
1
def self.visit(root, options, format)
-
new(options, format).send(:visit, root)
-
end
-
-
1
protected
-
-
1
def initialize(options, format)
-
@options = options
-
@format = format
-
@tabs = 0
-
end
-
-
1
def visit_children(parent)
-
@tabs += 1
-
return @format == :sass ? "\n" : " {}\n" if parent.children.empty?
-
(@format == :sass ? "\n" : " {\n") + super.join.rstrip + (@format == :sass ? "\n" : " }\n")
-
ensure
-
@tabs -= 1
-
end
-
-
# Ensures proper spacing between top-level nodes.
-
1
def visit_root(node)
-
Sass::Util.enum_cons(node.children + [nil], 2).map do |child, nxt|
-
visit(child) +
-
if nxt &&
-
(child.is_a?(Sass::Tree::CommentNode) &&
-
child.line + child.lines + 1 == nxt.line) ||
-
(child.is_a?(Sass::Tree::ImportNode) && nxt.is_a?(Sass::Tree::ImportNode) &&
-
child.line + 1 == nxt.line) ||
-
(child.is_a?(Sass::Tree::VariableNode) && nxt.is_a?(Sass::Tree::VariableNode) &&
-
child.line + 1 == nxt.line)
-
""
-
else
-
"\n"
-
end
-
end.join.rstrip + "\n"
-
end
-
-
1
def visit_charset(node)
-
"#{tab_str}@charset \"#{node.name}\"#{semi}\n"
-
end
-
-
1
def visit_comment(node)
-
value = node.value.map do |r|
-
next r if r.is_a?(String)
-
"\#{#{r.to_sass(@options)}}"
-
end.join
-
-
content = if @format == :sass
-
content = value.gsub(/\*\/$/, '').rstrip
-
if content =~ /\A[ \t]/
-
# Re-indent SCSS comments like this:
-
# /* foo
-
# bar
-
# baz */
-
content.gsub!(/^/, ' ')
-
content.sub!(/\A([ \t]*)\/\*/, '/*\1')
-
end
-
-
content =
-
unless content.include?("\n")
-
content
-
else
-
content.gsub!(/\n( \*|\/\/)/, "\n ")
-
spaces = content.scan(/\n( *)/).map {|s| s.first.size}.min
-
sep = node.silent ? "\n//" : "\n *"
-
if spaces >= 2
-
content.gsub(/\n /, sep)
-
else
-
content.gsub(/\n#{' ' * spaces}/, sep)
-
end
-
end
-
-
content.gsub!(/\A\/\*/, '//') if node.silent
-
content.gsub!(/^/, tab_str)
-
content.rstrip + "\n"
-
else
-
spaces = (' ' * [@tabs - value[/^ */].size, 0].max)
-
content = if node.silent
-
value.gsub(/^[\/ ]\*/, '//').gsub(/ *\*\/$/, '')
-
else
-
value
-
end.gsub(/^/, spaces) + "\n"
-
content
-
end
-
if node.loud
-
if node.silent
-
content.gsub!(%r{^\s*(//!?)}, '//!')
-
else
-
content.sub!(%r{^\s*(/\*)}, '/*!')
-
end
-
end
-
content
-
end
-
-
1
def visit_debug(node)
-
"#{tab_str}@debug #{node.expr.to_sass(@options)}#{semi}\n"
-
end
-
-
1
def visit_directive(node)
-
res = "#{tab_str}#{node.value}"
-
return res + "#{semi}\n" unless node.has_children
-
res + yield + "\n"
-
end
-
-
1
def visit_each(node)
-
"#{tab_str}@each $#{dasherize(node.var)} in #{node.list.to_sass(@options)}#{yield}"
-
end
-
-
1
def visit_extend(node)
-
"#{tab_str}@extend #{selector_to_src(node.selector).lstrip}#{semi}\n"
-
end
-
-
1
def visit_for(node)
-
"#{tab_str}@for $#{dasherize(node.var)} from #{node.from.to_sass(@options)} " +
-
"#{node.exclusive ? "to" : "through"} #{node.to.to_sass(@options)}#{yield}"
-
end
-
-
1
def visit_function(node)
-
args = node.args.map do |v, d|
-
d ? "#{v.to_sass(@options)}: #{d.to_sass(@options)}" : v.to_sass(@options)
-
end.join(", ")
-
-
"#{tab_str}@function #{dasherize(node.name)}(#{args})#{yield}"
-
end
-
-
1
def visit_if(node)
-
name =
-
if !@is_else; "if"
-
elsif node.expr; "else if"
-
else; "else"
-
end
-
str = "#{tab_str}@#{name}"
-
str << " #{node.expr.to_sass(@options)}" if node.expr
-
str << yield
-
@is_else = true
-
str << visit(node.else) if node.else
-
str
-
ensure
-
@is_else = false
-
end
-
-
1
def visit_import(node)
-
quote = @format == :scss ? '"' : ''
-
"#{tab_str}@import #{quote}#{node.imported_filename}#{quote}#{semi}\n"
-
end
-
-
1
def visit_media(node)
-
"#{tab_str}@media #{node.query.join(', ')}#{yield}"
-
end
-
-
1
def visit_mixindef(node)
-
args =
-
if node.args.empty?
-
""
-
else
-
'(' + node.args.map do |v, d|
-
if d
-
"#{v.to_sass(@options)}: #{d.to_sass(@options)}"
-
else
-
v.to_sass(@options)
-
end
-
end.join(", ") + ')'
-
end
-
-
"#{tab_str}#{@format == :sass ? '=' : '@mixin '}#{dasherize(node.name)}#{args}#{yield}"
-
end
-
-
1
def visit_mixin(node)
-
unless node.args.empty? && node.keywords.empty?
-
args = node.args.map {|a| a.to_sass(@options)}.join(", ")
-
keywords = Sass::Util.hash_to_a(node.keywords).
-
map {|k, v| "$#{dasherize(k)}: #{v.to_sass(@options)}"}.join(', ')
-
arglist = "(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords})"
-
end
-
"#{tab_str}#{@format == :sass ? '+' : '@include '}#{dasherize(node.name)}#{arglist}#{semi}\n"
-
end
-
-
1
def visit_prop(node)
-
res = tab_str + node.declaration(@options, @format)
-
return res + semi + "\n" if node.children.empty?
-
res + yield.rstrip + semi + "\n"
-
end
-
-
1
def visit_return(node)
-
"#{tab_str}@return #{node.expr.to_sass(@options)}#{semi}\n"
-
end
-
-
1
def visit_rule(node)
-
if @format == :sass
-
name = selector_to_sass(node.rule)
-
name = "\\" + name if name[0] == ?:
-
name.gsub(/^/, tab_str) + yield
-
elsif @format == :scss
-
name = selector_to_scss(node.rule)
-
res = name + yield
-
if node.children.last.is_a?(Sass::Tree::CommentNode) && node.children.last.silent
-
res.slice!(-3..-1)
-
res << "\n" << tab_str << "}\n"
-
end
-
res
-
end
-
end
-
-
1
def visit_variable(node)
-
"#{tab_str}$#{dasherize(node.name)}: #{node.expr.to_sass(@options)}#{' !default' if node.guarded}#{semi}\n"
-
end
-
-
1
def visit_warn(node)
-
"#{tab_str}@warn #{node.expr.to_sass(@options)}#{semi}\n"
-
end
-
-
1
def visit_while(node)
-
"#{tab_str}@while #{node.expr.to_sass(@options)}#{yield}"
-
end
-
-
1
private
-
-
1
def selector_to_src(sel)
-
@format == :sass ? selector_to_sass(sel) : selector_to_scss(sel)
-
end
-
-
1
def selector_to_sass(sel)
-
sel.map do |r|
-
if r.is_a?(String)
-
r.gsub(/(,)?([ \t]*)\n\s*/) {$1 ? "#{$1}#{$2}\n" : " "}
-
else
-
"\#{#{r.to_sass(@options)}}"
-
end
-
end.join
-
end
-
-
1
def selector_to_scss(sel)
-
sel.map {|r| r.is_a?(String) ? r : "\#{#{r.to_sass(@options)}}"}.
-
join.gsub(/^[ \t]*/, tab_str).gsub(/[ \t]*$/, '')
-
end
-
-
1
def semi
-
@format == :sass ? "" : ";"
-
end
-
-
1
def tab_str
-
' ' * @tabs
-
end
-
-
1
def dasherize(s)
-
if @options[:dasherize]
-
s.gsub('_', '-')
-
else
-
s
-
end
-
end
-
end
-
# A visitor for converting a static Sass tree into a static CSS tree.
-
1
class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
-
# @param root [Tree::Node] The root node of the tree to visit.
-
# @return [(Tree::Node, Sass::Util::SubsetMap)] The resulting tree of static nodes
-
# *and* the extensions defined for this tree
-
1
def self.visit(root); super; end
-
-
1
protected
-
-
# Returns the immediate parent of the current node.
-
# @return [Tree::Node]
-
1
attr_reader :parent
-
-
1
def initialize
-
@extends = Sass::Util::SubsetMap.new
-
end
-
-
# If an exception is raised, this adds proper metadata to the backtrace.
-
1
def visit(node)
-
super(node)
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace(:filename => node.filename, :line => node.line)
-
raise e
-
end
-
-
# Keeps track of the current parent node.
-
1
def visit_children(parent)
-
with_parent parent do
-
parent.children = super.flatten
-
parent
-
end
-
end
-
-
# Runs a block of code with the current parent node
-
# replaced with the given node.
-
#
-
# @param parent [Tree::Node] The new parent for the duration of the block.
-
# @yield A block in which the parent is set to `parent`.
-
# @return [Object] The return value of the block.
-
1
def with_parent(parent)
-
old_parent, @parent = @parent, parent
-
yield
-
ensure
-
@parent = old_parent
-
end
-
-
# In Ruby 1.8, ensures that there's only one `@charset` directive
-
# and that it's at the top of the document.
-
#
-
# @return [(Tree::Node, Sass::Util::SubsetMap)] The resulting tree of static nodes
-
# *and* the extensions defined for this tree
-
1
def visit_root(node)
-
yield
-
-
if parent.nil?
-
# In Ruby 1.9 we can make all @charset nodes invisible
-
# and infer the final @charset from the encoding of the final string.
-
if Sass::Util.ruby1_8?
-
charset = node.children.find {|c| c.is_a?(Sass::Tree::CharsetNode)}
-
node.children.reject! {|c| c.is_a?(Sass::Tree::CharsetNode)}
-
node.children.unshift charset if charset
-
end
-
-
imports = Sass::Util.extract!(node.children) do |c|
-
c.is_a?(Sass::Tree::DirectiveNode) && c.value =~ /^@import /i
-
end
-
charset_and_index = Sass::Util.ruby1_8? &&
-
node.children.each_with_index.find {|c, _| c.is_a?(Sass::Tree::CharsetNode)}
-
if charset_and_index
-
index = charset_and_index.last
-
node.children = node.children[0..index] + imports + node.children[index+1..-1]
-
else
-
node.children = imports + node.children
-
end
-
end
-
-
return node, @extends
-
rescue Sass::SyntaxError => e
-
e.sass_template ||= node.template
-
raise e
-
end
-
-
# Registers an extension in the `@extends` subset map.
-
1
def visit_extend(node)
-
node.resolved_selector.members.each do |seq|
-
if seq.members.size > 1
-
raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: can't extend nested selectors")
-
end
-
-
sseq = seq.members.first
-
if !sseq.is_a?(Sass::Selector::SimpleSequence)
-
raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: invalid selector")
-
end
-
-
sel = sseq.members
-
parent.resolved_rules.members.each do |seq|
-
if !seq.members.last.is_a?(Sass::Selector::SimpleSequence)
-
raise Sass::SyntaxError.new("#{seq} can't extend: invalid selector")
-
end
-
-
@extends[sel] = seq
-
end
-
end
-
-
[]
-
end
-
-
# Modifies exception backtraces to include the imported file.
-
1
def visit_import(node)
-
# Don't use #visit_children to avoid adding the import node to the list of parents.
-
node.children.map {|c| visit(c)}.flatten
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace(:filename => node.children.first.filename)
-
e.add_backtrace(:filename => node.filename, :line => node.line)
-
raise e
-
end
-
-
# Bubbles the `@media` directive up through RuleNodes
-
# and merges it with other `@media` directives.
-
1
def visit_media(node)
-
if parent.is_a?(Sass::Tree::RuleNode)
-
new_rule = parent.dup
-
new_rule.children = node.children
-
node.children = with_parent(node) {Array(visit(new_rule))}
-
# If the last child is actually the end of the group,
-
# the parent's cssize will set it properly
-
node.children.last.group_end = false unless node.children.empty?
-
else
-
yield
-
end
-
-
media = node.children.select {|c| c.is_a?(Sass::Tree::MediaNode)}
-
node.children.reject! {|c| c.is_a?(Sass::Tree::MediaNode)}
-
media.each do |n|
-
n.query = node.query.map {|pq| n.query.map {|cq| "#{pq} and #{cq}"}}.flatten
-
end
-
(node.children.empty? ? [] : [node]) + media
-
end
-
-
# Asserts that all the mixin's children are valid in their new location.
-
1
def visit_mixin(node)
-
# Don't use #visit_children to avoid adding the mixin node to the list of parents.
-
node.children.map {|c| visit(c)}.flatten
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace(:mixin => node.name, :filename => node.filename, :line => node.line)
-
e.add_backtrace(:filename => node.filename, :line => node.line)
-
raise e
-
end
-
-
# Converts nested properties into flat properties
-
# and updates the indentation of the prop node based on the nesting level.
-
1
def visit_prop(node)
-
if parent.is_a?(Sass::Tree::PropNode)
-
node.resolved_name = "#{parent.resolved_name}-#{node.resolved_name}"
-
node.tabs = parent.tabs + (parent.resolved_value.empty? ? 0 : 1) if node.style == :nested
-
end
-
-
yield
-
-
result = node.children.dup
-
if !node.resolved_value.empty? || node.children.empty?
-
node.send(:check!)
-
result.unshift(node)
-
end
-
-
result
-
end
-
-
# Resolves parent references and nested selectors,
-
# and updates the indentation of the rule node based on the nesting level.
-
1
def visit_rule(node)
-
parent_resolved_rules = parent.is_a?(Sass::Tree::RuleNode) ? parent.resolved_rules : nil
-
# It's possible for resolved_rules to be set if we've duplicated this node during @media bubbling
-
node.resolved_rules ||= node.parsed_rules.resolve_parent_refs(parent_resolved_rules)
-
-
yield
-
-
rules = node.children.select {|c| c.is_a?(Sass::Tree::RuleNode) || c.is_a?(Sass::Tree::MediaNode)}
-
props = node.children.reject {|c| c.is_a?(Sass::Tree::RuleNode) || c.is_a?(Sass::Tree::MediaNode) || c.invisible?}
-
-
unless props.empty?
-
node.children = props
-
rules.each {|r| r.tabs += 1} if node.style == :nested
-
rules.unshift(node)
-
end
-
-
rules.last.group_end = true unless parent.is_a?(Sass::Tree::RuleNode) || rules.empty?
-
-
rules
-
end
-
end
-
# A visitor for copying the full structure of a Sass tree.
-
1
class Sass::Tree::Visitors::DeepCopy < Sass::Tree::Visitors::Base
-
1
protected
-
-
1
def visit(node)
-
super(node.dup)
-
end
-
-
1
def visit_children(parent)
-
parent.children = parent.children.map {|c| visit(c)}
-
parent
-
end
-
-
1
def visit_debug(node)
-
node.expr = node.expr.deep_copy
-
yield
-
end
-
-
1
def visit_each(node)
-
node.list = node.list.deep_copy
-
yield
-
end
-
-
1
def visit_extend(node)
-
node.selector = node.selector.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c}
-
yield
-
end
-
-
1
def visit_for(node)
-
node.from = node.from.deep_copy
-
node.to = node.to.deep_copy
-
yield
-
end
-
-
1
def visit_function(node)
-
node.args = node.args.map {|k, v| [k.deep_copy, v && v.deep_copy]}
-
yield
-
end
-
-
1
def visit_if(node)
-
node.expr = node.expr.deep_copy if node.expr
-
node.else = visit(node.else) if node.else
-
yield
-
end
-
-
1
def visit_mixindef(node)
-
node.args = node.args.map {|k, v| [k.deep_copy, v && v.deep_copy]}
-
yield
-
end
-
-
1
def visit_mixin(node)
-
node.args = node.args.map {|a| a.deep_copy}
-
node.keywords = Hash[node.keywords.map {|k, v| [k, v.deep_copy]}]
-
yield
-
end
-
-
1
def visit_prop(node)
-
node.name = node.name.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c}
-
node.value = node.value.deep_copy
-
yield
-
end
-
-
1
def visit_return(node)
-
node.expr = node.expr.deep_copy
-
yield
-
end
-
-
1
def visit_rule(node)
-
node.rule = node.rule.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c}
-
yield
-
end
-
-
1
def visit_variable(node)
-
node.expr = node.expr.deep_copy
-
yield
-
end
-
-
1
def visit_warn(node)
-
node.expr = node.expr.deep_copy
-
yield
-
end
-
-
1
def visit_while(node)
-
node.expr = node.expr.deep_copy
-
yield
-
end
-
end
-
# A visitor for converting a dynamic Sass tree into a static Sass tree.
-
1
class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
-
# @param root [Tree::Node] The root node of the tree to visit.
-
# @param environment [Sass::Environment] The lexical environment.
-
# @return [Tree::Node] The resulting tree of static nodes.
-
1
def self.visit(root, environment = Sass::Environment.new)
-
new(environment).send(:visit, root)
-
end
-
-
1
protected
-
-
1
def initialize(env)
-
@environment = env
-
end
-
-
# If an exception is raised, this add proper metadata to the backtrace.
-
1
def visit(node)
-
super(node.dup)
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace(:filename => node.filename, :line => node.line)
-
raise e
-
end
-
-
# Keeps track of the current environment.
-
1
def visit_children(parent)
-
with_environment Sass::Environment.new(@environment) do
-
parent.children = super.flatten
-
parent
-
end
-
end
-
-
# Runs a block of code with the current environment replaced with the given one.
-
#
-
# @param env [Sass::Environment] The new environment for the duration of the block.
-
# @yield A block in which the environment is set to `env`.
-
# @return [Object] The return value of the block.
-
1
def with_environment(env)
-
old_env, @environment = @environment, env
-
yield
-
ensure
-
@environment = old_env
-
end
-
-
# Sets the options on the environment if this is the top-level root.
-
1
def visit_root(node)
-
@environment.options = node.options if @environment.options.nil? || @environment.options.empty?
-
yield
-
rescue Sass::SyntaxError => e
-
e.sass_template ||= node.template
-
raise e
-
end
-
-
# Removes this node from the tree if it's a silent comment.
-
1
def visit_comment(node)
-
return [] if node.invisible?
-
check_for_loud_silent_comment node
-
check_for_comment_interp node
-
node.resolved_value = run_interp_no_strip(node.value)
-
node.resolved_value.gsub!(/\\([\\#])/, '\1')
-
node
-
end
-
-
# Prints the expression to STDERR.
-
1
def visit_debug(node)
-
res = node.expr.perform(@environment)
-
res = res.value if res.is_a?(Sass::Script::String)
-
if node.filename
-
$stderr.puts "#{node.filename}:#{node.line} DEBUG: #{res}"
-
else
-
$stderr.puts "Line #{node.line} DEBUG: #{res}"
-
end
-
[]
-
end
-
-
# Runs the child nodes once for each value in the list.
-
1
def visit_each(node)
-
list = node.list.perform(@environment)
-
-
with_environment Sass::Environment.new(@environment) do
-
list.to_a.map do |v|
-
@environment.set_local_var(node.var, v)
-
node.children.map {|c| visit(c)}
-
end.flatten
-
end
-
end
-
-
# Runs SassScript interpolation in the selector,
-
# and then parses the result into a {Sass::Selector::CommaSequence}.
-
1
def visit_extend(node)
-
parser = Sass::SCSS::CssParser.new(run_interp(node.selector), node.filename, node.line)
-
node.resolved_selector = parser.parse_selector
-
node
-
end
-
-
# Runs the child nodes once for each time through the loop, varying the variable each time.
-
1
def visit_for(node)
-
from = node.from.perform(@environment)
-
to = node.to.perform(@environment)
-
from.assert_int!
-
to.assert_int!
-
-
to = to.coerce(from.numerator_units, from.denominator_units)
-
range = Range.new(from.to_i, to.to_i, node.exclusive)
-
-
with_environment Sass::Environment.new(@environment) do
-
range.map do |i|
-
@environment.set_local_var(node.var,
-
Sass::Script::Number.new(i, from.numerator_units, from.denominator_units))
-
node.children.map {|c| visit(c)}
-
end.flatten
-
end
-
end
-
-
# Loads the function into the environment.
-
1
def visit_function(node)
-
@environment.set_function(node.name,
-
Sass::Callable.new(node.name, node.args, @environment, node.children))
-
[]
-
end
-
-
# Runs the child nodes if the conditional expression is true;
-
# otherwise, tries the else nodes.
-
1
def visit_if(node)
-
if node.expr.nil? || node.expr.perform(@environment).to_bool
-
yield
-
node.children
-
elsif node.else
-
visit(node.else)
-
else
-
[]
-
end
-
end
-
-
# Returns a static DirectiveNode if this is importing a CSS file,
-
# or parses and includes the imported Sass file.
-
1
def visit_import(node)
-
if path = node.css_import?
-
return Sass::Tree::DirectiveNode.new("@import url(#{path})")
-
end
-
file = node.imported_file
-
handle_import_loop!(node) if @environment.files_in_use.include?(file.options[:filename])
-
-
@environment.push_frame(:filename => node.filename, :line => node.line)
-
root = file.to_tree
-
Sass::Tree::Visitors::CheckNesting.visit(root)
-
node.children = root.children.map {|c| visit(c)}.flatten
-
node
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace(:filename => node.imported_file.options[:filename])
-
e.add_backtrace(:filename => node.filename, :line => node.line)
-
raise e
-
ensure
-
@environment.pop_frame
-
end
-
-
# Loads a mixin into the environment.
-
1
def visit_mixindef(node)
-
@environment.set_mixin(node.name,
-
Sass::Callable.new(node.name, node.args, @environment, node.children))
-
[]
-
end
-
-
# Runs a mixin.
-
1
def visit_mixin(node)
-
handle_include_loop!(node) if @environment.mixins_in_use.include?(node.name)
-
-
original_env = @environment
-
original_env.push_frame(:filename => node.filename, :line => node.line)
-
original_env.prepare_frame(:mixin => node.name)
-
raise Sass::SyntaxError.new("Undefined mixin '#{node.name}'.") unless mixin = @environment.mixin(node.name)
-
-
passed_args = node.args.dup
-
passed_keywords = node.keywords.dup
-
-
raise Sass::SyntaxError.new(<<END.gsub("\n", "")) if mixin.args.size < passed_args.size
-
Mixin #{node.name} takes #{mixin.args.size} argument#{'s' if mixin.args.size != 1}
-
but #{node.args.size} #{node.args.size == 1 ? 'was' : 'were'} passed.
-
END
-
-
passed_keywords.each do |name, value|
-
# TODO: Make this fast
-
unless mixin.args.find {|(var, default)| var.underscored_name == name}
-
raise Sass::SyntaxError.new("Mixin #{node.name} doesn't have an argument named $#{name}")
-
end
-
end
-
-
environment = mixin.args.zip(passed_args).
-
inject(Sass::Environment.new(mixin.environment)) do |env, ((var, default), value)|
-
env.set_local_var(var.name,
-
if value
-
value.perform(@environment)
-
elsif kv = passed_keywords[var.underscored_name]
-
kv.perform(@environment)
-
elsif default
-
default.perform(env)
-
end)
-
raise Sass::SyntaxError.new("Mixin #{node.name} is missing parameter #{var.inspect}.") unless env.var(var.name)
-
env
-
end
-
-
with_environment(environment) {node.children = mixin.tree.map {|c| visit(c)}.flatten}
-
node
-
rescue Sass::SyntaxError => e
-
if original_env # Don't add backtrace info if this is an @include loop
-
e.modify_backtrace(:mixin => node.name, :line => node.line)
-
e.add_backtrace(:line => node.line)
-
end
-
raise e
-
ensure
-
original_env.pop_frame if original_env
-
end
-
-
# Runs any SassScript that may be embedded in a property.
-
1
def visit_prop(node)
-
node.resolved_name = run_interp(node.name)
-
val = node.value.perform(@environment)
-
node.resolved_value = val.to_s
-
yield
-
end
-
-
# Returns the value of the expression.
-
1
def visit_return(node)
-
throw :_sass_return, node.expr.perform(@environment)
-
end
-
-
# Runs SassScript interpolation in the selector,
-
# and then parses the result into a {Sass::Selector::CommaSequence}.
-
1
def visit_rule(node)
-
rule = node.rule
-
rule = rule.map {|e| e.is_a?(String) && e != ' ' ? e.strip : e} if node.style == :compressed
-
parser = Sass::SCSS::StaticParser.new(run_interp(node.rule), node.filename, node.line)
-
node.parsed_rules ||= parser.parse_selector
-
if node.options[:trace_selectors]
-
@environment.push_frame(:filename => node.filename, :line => node.line)
-
node.stack_trace = @environment.stack_trace
-
@environment.pop_frame
-
end
-
yield
-
end
-
-
# Loads the new variable value into the environment.
-
1
def visit_variable(node)
-
return [] if node.guarded && !@environment.var(node.name).nil?
-
val = node.expr.perform(@environment)
-
@environment.set_var(node.name, val)
-
[]
-
end
-
-
# Prints the expression to STDERR with a stylesheet trace.
-
1
def visit_warn(node)
-
@environment.push_frame(:filename => node.filename, :line => node.line)
-
res = node.expr.perform(@environment)
-
res = res.value if res.is_a?(Sass::Script::String)
-
msg = "WARNING: #{res}\n "
-
msg << @environment.stack_trace.join("\n ")
-
# JRuby doesn't automatically add a newline for #warn
-
msg << (RUBY_PLATFORM =~ /java/ ? "\n\n" : "\n")
-
Sass::Util.sass_warn msg
-
[]
-
ensure
-
@environment.pop_frame
-
end
-
-
# Runs the child nodes until the continuation expression becomes false.
-
1
def visit_while(node)
-
children = []
-
with_environment Sass::Environment.new(@environment) do
-
children += node.children.map {|c| visit(c)} while node.expr.perform(@environment).to_bool
-
end
-
children.flatten
-
end
-
-
1
def visit_directive(node)
-
if node.value['#{']
-
if node.value =~ /^@import (?!url\()/
-
Sass::Util.sass_warn <<WARNING
-
DEPRECATION WARNING on line #{node.line}#{" of #{node.filename}" if node.filename}:
-
@import directives using \#{} interpolation will need to use url() in Sass 3.2.
-
For example:
-
-
@import url("http://\#{$url}/style.css");
-
WARNING
-
end
-
node.value = run_interp(Sass::Engine.parse_interp(node.value, node.line, 0, node.options))
-
end
-
yield
-
node
-
end
-
-
1
private
-
-
1
def run_interp_no_strip(text)
-
text.map do |r|
-
next r if r.is_a?(String)
-
val = r.perform(@environment)
-
# Interpolated strings should never render with quotes
-
next val.value if val.is_a?(Sass::Script::String)
-
val.to_s
-
end.join
-
end
-
-
1
def run_interp(text)
-
run_interp_no_strip(text).strip
-
end
-
-
1
def handle_include_loop!(node)
-
msg = "An @include loop has been found:"
-
mixins = @environment.stack.map {|s| s[:mixin]}.compact
-
raise Sass::SyntaxError.new("#{msg} #{node.name} includes itself") if mixins.size == 1
-
-
mixins << node.name
-
msg << "\n" << Sass::Util.enum_cons(mixins, 2).map do |m1, m2|
-
" #{m1} includes #{m2}"
-
end.join("\n")
-
raise Sass::SyntaxError.new(msg)
-
end
-
-
1
def handle_import_loop!(node)
-
msg = "An @import loop has been found:"
-
files = @environment.stack.map {|s| s[:filename]}.compact
-
if node.filename == node.imported_file.options[:filename]
-
raise Sass::SyntaxError.new("#{msg} #{node.filename} imports itself")
-
end
-
-
files << node.filename << node.imported_file.options[:filename]
-
msg << "\n" << Sass::Util.enum_cons(files, 2).map do |m1, m2|
-
" #{m1} imports #{m2}"
-
end.join("\n")
-
raise Sass::SyntaxError.new(msg)
-
end
-
-
1
def check_for_loud_silent_comment(node)
-
return unless node.loud && node.silent
-
Sass::Util.sass_warn <<MESSAGE
-
WARNING:
-
On line #{node.line}#{" of '#{node.filename}'" if node.filename}
-
`//` comments will no longer be allowed to use the `!` flag in Sass 3.2.
-
Please change to `/*` comments.
-
MESSAGE
-
end
-
-
1
def check_for_comment_interp(node)
-
return if node.loud
-
node.value.each do |e|
-
next unless e.is_a?(String)
-
e.scan(/(\\*)#\{/) do |esc|
-
Sass::Util.sass_warn <<MESSAGE if esc.first.size.even?
-
WARNING:
-
On line #{node.line}#{" of '#{node.filename}'" if node.filename}
-
Comments will evaluate the contents of interpolations (\#{ ... }) in Sass 3.2.
-
Please escape the interpolation by adding a backslash before the `#`.
-
MESSAGE
-
return
-
end
-
end
-
end
-
end
-
# A visitor for setting options on the Sass tree
-
1
class Sass::Tree::Visitors::SetOptions < Sass::Tree::Visitors::Base
-
# @param root [Tree::Node] The root node of the tree to visit.
-
# @param options [{Symbol => Object}] The options has to set.
-
1
def self.visit(root, options); new(options).send(:visit, root); end
-
-
1
protected
-
-
1
def initialize(options)
-
@options = options
-
end
-
-
1
def visit(node)
-
node.instance_variable_set('@options', @options)
-
super
-
end
-
-
1
def visit_debug(node)
-
node.expr.options = @options
-
yield
-
end
-
-
1
def visit_each(node)
-
node.list.options = @options
-
yield
-
end
-
-
1
def visit_extend(node)
-
node.selector.each {|c| c.options = @options if c.is_a?(Sass::Script::Node)}
-
yield
-
end
-
-
1
def visit_for(node)
-
node.from.options = @options
-
node.to.options = @options
-
yield
-
end
-
-
1
def visit_function(node)
-
node.args.each do |k, v|
-
k.options = @options
-
v.options = @options if v
-
end
-
yield
-
end
-
-
1
def visit_if(node)
-
node.expr.options = @options if node.expr
-
visit(node.else) if node.else
-
yield
-
end
-
-
1
def visit_mixindef(node)
-
node.args.each do |k, v|
-
k.options = @options
-
v.options = @options if v
-
end
-
yield
-
end
-
-
1
def visit_mixin(node)
-
node.args.each {|a| a.options = @options}
-
node.keywords.each {|k, v| v.options = @options}
-
yield
-
end
-
-
1
def visit_prop(node)
-
node.name.each {|c| c.options = @options if c.is_a?(Sass::Script::Node)}
-
node.value.options = @options
-
yield
-
end
-
-
1
def visit_return(node)
-
node.expr.options = @options
-
yield
-
end
-
-
1
def visit_rule(node)
-
node.rule.each {|c| c.options = @options if c.is_a?(Sass::Script::Node)}
-
yield
-
end
-
-
1
def visit_variable(node)
-
node.expr.options = @options
-
yield
-
end
-
-
1
def visit_warn(node)
-
node.expr.options = @options
-
yield
-
end
-
-
1
def visit_while(node)
-
node.expr.options = @options
-
yield
-
end
-
end
-
# A visitor for converting a Sass tree into CSS.
-
1
class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
-
1
protected
-
-
1
def initialize
-
@tabs = 0
-
end
-
-
1
def visit(node)
-
super
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace(:filename => node.filename, :line => node.line)
-
raise e
-
end
-
-
1
def with_tabs(tabs)
-
old_tabs, @tabs = @tabs, tabs
-
yield
-
ensure
-
@tabs = old_tabs
-
end
-
-
1
def visit_root(node)
-
result = String.new
-
node.children.each do |child|
-
next if child.invisible?
-
child_str = visit(child)
-
result << child_str + (node.style == :compressed ? '' : "\n")
-
end
-
result.rstrip!
-
return "" if result.empty?
-
result << "\n"
-
unless Sass::Util.ruby1_8? || result.ascii_only?
-
if node.children.first.is_a?(Sass::Tree::CharsetNode)
-
begin
-
encoding = node.children.first.name
-
# Default to big-endian encoding, because we have to decide somehow
-
encoding << 'BE' if encoding =~ /\Autf-(16|32)\Z/i
-
result = result.encode(Encoding.find(encoding))
-
rescue EncodingError
-
end
-
end
-
-
result = "@charset \"#{result.encoding.name}\";#{
-
node.style == :compressed ? '' : "\n"
-
}".encode(result.encoding) + result
-
end
-
result
-
rescue Sass::SyntaxError => e
-
e.sass_template ||= node.template
-
raise e
-
end
-
-
1
def visit_charset(node)
-
"@charset \"#{node.name}\";"
-
end
-
-
1
def visit_comment(node)
-
return if node.invisible?
-
spaces = (' ' * [@tabs - node.resolved_value[/^ */].size, 0].max)
-
-
content = node.resolved_value.gsub(/^/, spaces).gsub(%r{^(\s*)//(.*)$}) do |md|
-
"#{$1}/*#{$2} */"
-
end
-
content.gsub!(/\n +(\* *(?!\/))?/, ' ') if (node.style == :compact || node.style == :compressed) && !node.loud
-
content
-
end
-
-
1
def visit_directive(node)
-
was_in_directive = @in_directive
-
return node.value + ";" unless node.has_children
-
return node.value + " {}" if node.children.empty?
-
@in_directive = @in_directive || !node.is_a?(Sass::Tree::MediaNode)
-
result = if node.style == :compressed
-
"#{node.value}{"
-
else
-
"#{' ' * @tabs}#{node.value} {" + (node.style == :compact ? ' ' : "\n")
-
end
-
was_prop = false
-
first = true
-
node.children.each do |child|
-
next if child.invisible?
-
if node.style == :compact
-
if child.is_a?(Sass::Tree::PropNode)
-
with_tabs(first || was_prop ? 0 : @tabs + 1) {result << visit(child) << ' '}
-
else
-
result[-1] = "\n" if was_prop
-
rendered = with_tabs(@tabs + 1) {visit(child).dup}
-
rendered = rendered.lstrip if first
-
result << rendered.rstrip + "\n"
-
end
-
was_prop = child.is_a?(Sass::Tree::PropNode)
-
first = false
-
elsif node.style == :compressed
-
result << (was_prop ? ";" : "") << with_tabs(0) {visit(child)}
-
was_prop = child.is_a?(Sass::Tree::PropNode)
-
else
-
result << with_tabs(@tabs + 1) {visit(child)} + "\n"
-
end
-
end
-
result.rstrip + if node.style == :compressed
-
"}"
-
else
-
(node.style == :expanded ? "\n" : " ") + "}\n"
-
end
-
ensure
-
@in_directive = was_in_directive
-
end
-
-
1
def visit_media(node)
-
str = with_tabs(@tabs + node.tabs) {visit_directive(node)}
-
str.gsub!(/\n\Z/, '') unless node.style == :compressed || node.group_end
-
str
-
end
-
-
1
def visit_prop(node)
-
tab_str = ' ' * (@tabs + node.tabs)
-
if node.style == :compressed
-
"#{tab_str}#{node.resolved_name}:#{node.resolved_value}"
-
else
-
"#{tab_str}#{node.resolved_name}: #{node.resolved_value};"
-
end
-
end
-
-
1
def visit_rule(node)
-
with_tabs(@tabs + node.tabs) do
-
rule_separator = node.style == :compressed ? ',' : ', '
-
line_separator =
-
case node.style
-
when :nested, :expanded; "\n"
-
when :compressed; ""
-
else; " "
-
end
-
rule_indent = ' ' * @tabs
-
per_rule_indent, total_indent = [:nested, :expanded].include?(node.style) ? [rule_indent, ''] : ['', rule_indent]
-
-
joined_rules = node.resolved_rules.members.map do |seq|
-
rule_part = seq.to_a.join
-
if node.style == :compressed
-
rule_part.gsub!(/([^,])\s*\n\s*/m, '\1 ')
-
rule_part.gsub!(/\s*([,+>])\s*/m, '\1')
-
rule_part.strip!
-
end
-
rule_part
-
end.join(rule_separator)
-
-
joined_rules.sub!(/\A\s*/, per_rule_indent)
-
joined_rules.gsub!(/\s*\n\s*/, "#{line_separator}#{per_rule_indent}")
-
total_rule = total_indent << joined_rules
-
-
to_return = ''
-
old_spaces = ' ' * @tabs
-
spaces = ' ' * (@tabs + 1)
-
if node.style != :compressed
-
if node.options[:debug_info] && !@in_directive
-
to_return << visit(debug_info_rule(node.debug_info, node.options)) << "\n"
-
elsif node.options[:trace_selectors]
-
to_return << "#{old_spaces}/* "
-
to_return << node.stack_trace.join("\n #{old_spaces}")
-
to_return << " */\n"
-
elsif node.options[:line_comments]
-
to_return << "#{old_spaces}/* line #{node.line}"
-
-
if node.filename
-
relative_filename = if node.options[:css_filename]
-
begin
-
Pathname.new(node.filename).relative_path_from(
-
Pathname.new(File.dirname(node.options[:css_filename]))).to_s
-
rescue ArgumentError
-
nil
-
end
-
end
-
relative_filename ||= node.filename
-
to_return << ", #{relative_filename}"
-
end
-
-
to_return << " */\n"
-
end
-
end
-
-
if node.style == :compact
-
properties = with_tabs(0) {node.children.map {|a| visit(a)}.join(' ')}
-
to_return << "#{total_rule} { #{properties} }#{"\n" if node.group_end}"
-
elsif node.style == :compressed
-
properties = with_tabs(0) {node.children.map {|a| visit(a)}.join(';')}
-
to_return << "#{total_rule}{#{properties}}"
-
else
-
properties = with_tabs(@tabs + 1) {node.children.map {|a| visit(a)}.join("\n")}
-
end_props = (node.style == :expanded ? "\n" + old_spaces : ' ')
-
to_return << "#{total_rule} {\n#{properties}#{end_props}}#{"\n" if node.group_end}"
-
end
-
-
to_return
-
end
-
end
-
-
1
private
-
-
1
def debug_info_rule(debug_info, options)
-
node = Sass::Tree::DirectiveNode.new("@media -sass-debug-info")
-
Sass::Util.hash_to_a(debug_info.map {|k, v| [k.to_s, v.to_s]}).each do |k, v|
-
rule = Sass::Tree::RuleNode.new([""])
-
rule.resolved_rules = Sass::Selector::CommaSequence.new(
-
[Sass::Selector::Sequence.new(
-
[Sass::Selector::SimpleSequence.new(
-
[Sass::Selector::Element.new(k.to_s.gsub(/[^\w-]/, "\\\\\\0"), nil)])
-
])
-
])
-
prop = Sass::Tree::PropNode.new([""], Sass::Script::String.new(''), :new)
-
prop.resolved_name = "font-family"
-
prop.resolved_value = Sass::SCSS::RX.escape_ident(v.to_s)
-
rule << prop
-
node << rule
-
end
-
node.options = options.merge(:debug_info => false, :line_comments => false, :style => :compressed)
-
node
-
end
-
end
-
1
module Sass
-
1
module Tree
-
# A dynamic node representing a Sass `@warn` statement.
-
#
-
# @see Sass::Tree
-
1
class WarnNode < Node
-
# The expression to print.
-
# @return [Script::Node]
-
1
attr_accessor :expr
-
-
# @param expr [Script::Node] The expression to print
-
1
def initialize(expr)
-
@expr = expr
-
super()
-
end
-
end
-
end
-
end
-
1
require 'sass/tree/node'
-
-
1
module Sass::Tree
-
# A dynamic node representing a Sass `@while` loop.
-
#
-
# @see Sass::Tree
-
1
class WhileNode < Node
-
# The parse tree for the continuation expression.
-
# @return [Script::Node]
-
1
attr_accessor :expr
-
-
# @param expr [Script::Node] See \{#expr}
-
1
def initialize(expr)
-
@expr = expr
-
super()
-
end
-
end
-
end
-
1
require 'erb'
-
1
require 'set'
-
1
require 'enumerator'
-
1
require 'stringio'
-
1
require 'rbconfig'
-
-
1
require 'sass/root'
-
1
require 'sass/util/subset_map'
-
-
1
module Sass
-
# A module containing various useful functions.
-
1
module Util
-
1
extend self
-
-
# An array of ints representing the Ruby version number.
-
# @api public
-
4
RUBY_VERSION = ::RUBY_VERSION.split(".").map {|s| s.to_i}
-
-
# The Ruby engine we're running under. Defaults to `"ruby"`
-
# if the top-level constant is undefined.
-
# @api public
-
1
RUBY_ENGINE = defined?(::RUBY_ENGINE) ? ::RUBY_ENGINE : "ruby"
-
-
# Returns the path of a file relative to the Sass root directory.
-
#
-
# @param file [String] The filename relative to the Sass root
-
# @return [String] The filename relative to the the working directory
-
1
def scope(file)
-
5
File.join(Sass::ROOT_DIR, file)
-
end
-
-
# Converts an array of `[key, value]` pairs to a hash.
-
#
-
# @example
-
# to_hash([[:foo, "bar"], [:baz, "bang"]])
-
# #=> {:foo => "bar", :baz => "bang"}
-
# @param arr [Array<(Object, Object)>] An array of pairs
-
# @return [Hash] A hash
-
1
def to_hash(arr)
-
5
Hash[arr.compact]
-
end
-
-
# Maps the keys in a hash according to a block.
-
#
-
# @example
-
# map_keys({:foo => "bar", :baz => "bang"}) {|k| k.to_s}
-
# #=> {"foo" => "bar", "baz" => "bang"}
-
# @param hash [Hash] The hash to map
-
# @yield [key] A block in which the keys are transformed
-
# @yieldparam key [Object] The key that should be mapped
-
# @yieldreturn [Object] The new value for the key
-
# @return [Hash] The mapped hash
-
# @see #map_vals
-
# @see #map_hash
-
1
def map_keys(hash)
-
to_hash(hash.map {|k, v| [yield(k), v]})
-
end
-
-
# Maps the values in a hash according to a block.
-
#
-
# @example
-
# map_values({:foo => "bar", :baz => "bang"}) {|v| v.to_sym}
-
# #=> {:foo => :bar, :baz => :bang}
-
# @param hash [Hash] The hash to map
-
# @yield [value] A block in which the values are transformed
-
# @yieldparam value [Object] The value that should be mapped
-
# @yieldreturn [Object] The new value for the value
-
# @return [Hash] The mapped hash
-
# @see #map_keys
-
# @see #map_hash
-
1
def map_vals(hash)
-
17
to_hash(hash.map {|k, v| [k, yield(v)]})
-
end
-
-
# Maps the key-value pairs of a hash according to a block.
-
#
-
# @example
-
# map_hash({:foo => "bar", :baz => "bang"}) {|k, v| [k.to_s, v.to_sym]}
-
# #=> {"foo" => :bar, "baz" => :bang}
-
# @param hash [Hash] The hash to map
-
# @yield [key, value] A block in which the key-value pairs are transformed
-
# @yieldparam [key] The hash key
-
# @yieldparam [value] The hash value
-
# @yieldreturn [(Object, Object)] The new value for the `[key, value]` pair
-
# @return [Hash] The mapped hash
-
# @see #map_keys
-
# @see #map_vals
-
1
def map_hash(hash)
-
# Using &block here completely hoses performance on 1.8.
-
65
to_hash(hash.map {|k, v| yield k, v})
-
end
-
-
# Computes the powerset of the given array.
-
# This is the set of all subsets of the array.
-
#
-
# @example
-
# powerset([1, 2, 3]) #=>
-
# Set[Set[], Set[1], Set[2], Set[3], Set[1, 2], Set[2, 3], Set[1, 3], Set[1, 2, 3]]
-
# @param arr [Enumerable]
-
# @return [Set<Set>] The subsets of `arr`
-
1
def powerset(arr)
-
arr.inject([Set.new].to_set) do |powerset, el|
-
new_powerset = Set.new
-
powerset.each do |subset|
-
new_powerset << subset
-
new_powerset << subset + [el]
-
end
-
new_powerset
-
end
-
end
-
-
# Restricts a number to falling within a given range.
-
# Returns the number if it falls within the range,
-
# or the closest value in the range if it doesn't.
-
#
-
# @param value [Numeric]
-
# @param range [Range<Numeric>]
-
# @return [Numeric]
-
1
def restrict(value, range)
-
[[value, range.first].max, range.last].min
-
end
-
-
# Concatenates all strings that are adjacent in an array,
-
# while leaving other elements as they are.
-
#
-
# @example
-
# merge_adjacent_strings([1, "foo", "bar", 2, "baz"])
-
# #=> [1, "foobar", 2, "baz"]
-
# @param arr [Array]
-
# @return [Array] The enumerable with strings merged
-
1
def merge_adjacent_strings(arr)
-
# Optimize for the common case of one element
-
return arr if arr.size < 2
-
arr.inject([]) do |a, e|
-
if e.is_a?(String)
-
if a.last.is_a?(String)
-
a.last << e
-
else
-
a << e.dup
-
end
-
else
-
a << e
-
end
-
a
-
end
-
end
-
-
# Intersperses a value in an enumerable, as would be done with `Array#join`
-
# but without concatenating the array together afterwards.
-
#
-
# @param enum [Enumerable]
-
# @param val
-
# @return [Array]
-
1
def intersperse(enum, val)
-
enum.inject([]) {|a, e| a << e << val}[0...-1]
-
end
-
-
# Substitutes a sub-array of one array with another sub-array.
-
#
-
# @param ary [Array] The array in which to make the substitution
-
# @param from [Array] The sequence of elements to replace with `to`
-
# @param to [Array] The sequence of elements to replace `from` with
-
1
def substitute(ary, from, to)
-
res = ary.dup
-
i = 0
-
while i < res.size
-
if res[i...i+from.size] == from
-
res[i...i+from.size] = to
-
end
-
i += 1
-
end
-
res
-
end
-
-
# Destructively strips whitespace from the beginning and end
-
# of the first and last elements, respectively,
-
# in the array (if those elements are strings).
-
#
-
# @param arr [Array]
-
# @return [Array] `arr`
-
1
def strip_string_array(arr)
-
arr.first.lstrip! if arr.first.is_a?(String)
-
arr.last.rstrip! if arr.last.is_a?(String)
-
arr
-
end
-
-
# Return an array of all possible paths through the given arrays.
-
#
-
# @param arrs [Array<Array>]
-
# @return [Array<Arrays>]
-
#
-
# @example
-
# paths([[1, 2], [3, 4], [5]]) #=>
-
# # [[1, 3, 5],
-
# # [2, 3, 5],
-
# # [1, 4, 5],
-
# # [2, 4, 5]]
-
1
def paths(arrs)
-
arrs.inject([[]]) do |paths, arr|
-
flatten(arr.map {|e| paths.map {|path| path + [e]}}, 1)
-
end
-
end
-
-
# Computes a single longest common subsequence for `x` and `y`.
-
# If there are more than one longest common subsequences,
-
# the one returned is that which starts first in `x`.
-
#
-
# @param x [Array]
-
# @param y [Array]
-
# @yield [a, b] An optional block to use in place of a check for equality
-
# between elements of `x` and `y`.
-
# @yieldreturn [Object, nil] If the two values register as equal,
-
# this will return the value to use in the LCS array.
-
# @return [Array] The LCS
-
1
def lcs(x, y, &block)
-
x = [nil, *x]
-
y = [nil, *y]
-
block ||= proc {|a, b| a == b && a}
-
lcs_backtrace(lcs_table(x, y, &block), x, y, x.size-1, y.size-1, &block)
-
end
-
-
# Converts a Hash to an Array. This is usually identical to `Hash#to_a`,
-
# with the following exceptions:
-
#
-
# * In Ruby 1.8, `Hash#to_a` is not deterministically ordered, but this is.
-
# * In Ruby 1.9 when running tests, this is ordered in the same way it would
-
# be under Ruby 1.8 (sorted key order rather than insertion order).
-
#
-
# @param hash [Hash]
-
# @return [Array]
-
1
def hash_to_a(hash)
-
return hash.to_a unless ruby1_8? || defined?(Test::Unit)
-
return hash.sort_by {|k, v| k}
-
end
-
-
# Returns information about the caller of the previous method.
-
#
-
# @param entry [String] An entry in the `#caller` list, or a similarly formatted string
-
# @return [[String, Fixnum, (String, nil)]] An array containing the filename, line, and method name of the caller.
-
# The method name may be nil
-
1
def caller_info(entry = caller[1])
-
info = entry.scan(/^(.*?):(-?.*?)(?::.*`(.+)')?$/).first
-
info[1] = info[1].to_i
-
# This is added by Rubinius to designate a block, but we don't care about it.
-
info[2].sub!(/ \{\}\Z/, '') if info[2]
-
info
-
end
-
-
# Returns whether one version string represents a more recent version than another.
-
#
-
# @param v1 [String] A version string.
-
# @param v2 [String] Another version string.
-
# @return [Boolean]
-
1
def version_gt(v1, v2)
-
# Construct an array to make sure the shorter version is padded with nil
-
1
Array.new([v1.length, v2.length].max).zip(v1.split("."), v2.split(".")) do |_, p1, p2|
-
4
p1 ||= "0"
-
4
p2 ||= "0"
-
4
release1 = p1 =~ /^[0-9]+$/
-
4
release2 = p2 =~ /^[0-9]+$/
-
4
if release1 && release2
-
# Integer comparison if both are full releases
-
3
p1, p2 = p1.to_i, p2.to_i
-
3
next if p1 == p2
-
return p1 > p2
-
elsif !release1 && !release2
-
# String comparison if both are prereleases
-
next if p1 == p2
-
return p1 > p2
-
else
-
# If only one is a release, that one is newer
-
1
return release1
-
end
-
end
-
end
-
-
# Returns whether one version string represents the same or a more
-
# recent version than another.
-
#
-
# @param v1 [String] A version string.
-
# @param v2 [String] Another version string.
-
# @return [Boolean]
-
1
def version_geq(v1, v2)
-
1
version_gt(v1, v2) || !version_gt(v2, v1)
-
end
-
-
# Throws a NotImplementedError for an abstract method.
-
#
-
# @param obj [Object] `self`
-
# @raise [NotImplementedError]
-
1
def abstract(obj)
-
raise NotImplementedError.new("#{obj.class} must implement ##{caller_info[2]}")
-
end
-
-
# Silence all output to STDERR within a block.
-
#
-
# @yield A block in which no output will be printed to STDERR
-
1
def silence_warnings
-
the_real_stderr, $stderr = $stderr, StringIO.new
-
yield
-
ensure
-
$stderr = the_real_stderr
-
end
-
-
1
@@silence_warnings = false
-
# Silences all Sass warnings within a block.
-
#
-
# @yield A block in which no Sass warnings will be printed
-
1
def silence_sass_warnings
-
old_level, Sass.logger.log_level = Sass.logger.log_level, :error
-
yield
-
ensure
-
Sass.logger.log_level = old_level
-
end
-
-
# The same as `Kernel#warn`, but is silenced by \{#silence\_sass\_warnings}.
-
#
-
# @param msg [String]
-
1
def sass_warn(msg)
-
Sass.logger.warn(msg)
-
end
-
-
## Cross Rails Version Compatibility
-
-
# Returns the root of the Rails application,
-
# if this is running in a Rails context.
-
# Returns `nil` if no such root is defined.
-
#
-
# @return [String, nil]
-
1
def rails_root
-
if defined?(::Rails.root)
-
return ::Rails.root.to_s if ::Rails.root
-
raise "ERROR: Rails.root is nil!"
-
end
-
return RAILS_ROOT.to_s if defined?(RAILS_ROOT)
-
return nil
-
end
-
-
# Returns the environment of the Rails application,
-
# if this is running in a Rails context.
-
# Returns `nil` if no such environment is defined.
-
#
-
# @return [String, nil]
-
1
def rails_env
-
return ::Rails.env.to_s if defined?(::Rails.env)
-
return RAILS_ENV.to_s if defined?(RAILS_ENV)
-
return nil
-
end
-
-
# Returns whether this environment is using ActionPack
-
# version 3.0.0 or greater.
-
#
-
# @return [Boolean]
-
1
def ap_geq_3?
-
ap_geq?("3.0.0.beta1")
-
end
-
-
# Returns whether this environment is using ActionPack
-
# of a version greater than or equal to that specified.
-
#
-
# @param version [String] The string version number to check against.
-
# Should be greater than or equal to Rails 3,
-
# because otherwise ActionPack::VERSION isn't autoloaded
-
# @return [Boolean]
-
1
def ap_geq?(version)
-
# The ActionPack module is always loaded automatically in Rails >= 3
-
1
return false unless defined?(ActionPack) && defined?(ActionPack::VERSION) &&
-
defined?(ActionPack::VERSION::STRING)
-
-
1
version_geq(ActionPack::VERSION::STRING, version)
-
end
-
-
# Returns an ActionView::Template* class.
-
# In pre-3.0 versions of Rails, most of these classes
-
# were of the form `ActionView::TemplateFoo`,
-
# while afterwards they were of the form `ActionView;:Template::Foo`.
-
#
-
# @param name [#to_s] The name of the class to get.
-
# For example, `:Error` will return `ActionView::TemplateError`
-
# or `ActionView::Template::Error`.
-
1
def av_template_class(name)
-
return ActionView.const_get("Template#{name}") if ActionView.const_defined?("Template#{name}")
-
return ActionView::Template.const_get(name.to_s)
-
end
-
-
## Cross-OS Compatibility
-
-
# Whether or not this is running on Windows.
-
#
-
# @return [Boolean]
-
1
def windows?
-
RbConfig::CONFIG['host_os'] =~ /mswin|windows|mingw/i
-
end
-
-
# Whether or not this is running on IronRuby.
-
#
-
# @return [Boolean]
-
1
def ironruby?
-
4
RUBY_ENGINE == "ironruby"
-
end
-
-
## Cross-Ruby-Version Compatibility
-
-
# Whether or not this is running under Ruby 1.8 or lower.
-
#
-
# Note that IronRuby counts as Ruby 1.8,
-
# because it doesn't support the Ruby 1.9 encoding API.
-
#
-
# @return [Boolean]
-
1
def ruby1_8?
-
# IronRuby says its version is 1.9, but doesn't support any of the encoding APIs.
-
# We have to fall back to 1.8 behavior.
-
4
ironruby? || (Sass::Util::RUBY_VERSION[0] == 1 && Sass::Util::RUBY_VERSION[1] < 9)
-
end
-
-
# Whether or not this is running under Ruby 1.8.6 or lower.
-
# Note that lower versions are not officially supported.
-
#
-
# @return [Boolean]
-
1
def ruby1_8_6?
-
ruby1_8? && Sass::Util::RUBY_VERSION[2] < 7
-
end
-
-
# Checks that the encoding of a string is valid in Ruby 1.9
-
# and cleans up potential encoding gotchas like the UTF-8 BOM.
-
# If it's not, yields an error string describing the invalid character
-
# and the line on which it occurrs.
-
#
-
# @param str [String] The string of which to check the encoding
-
# @yield [msg] A block in which an encoding error can be raised.
-
# Only yields if there is an encoding error
-
# @yieldparam msg [String] The error message to be raised
-
# @return [String] `str`, potentially with encoding gotchas like BOMs removed
-
1
def check_encoding(str)
-
if ruby1_8?
-
return str.gsub(/\A\xEF\xBB\xBF/, '') # Get rid of the UTF-8 BOM
-
elsif str.valid_encoding?
-
# Get rid of the Unicode BOM if possible
-
if str.encoding.name =~ /^UTF-(8|16|32)(BE|LE)?$/
-
return str.gsub(Regexp.new("\\A\uFEFF".encode(str.encoding.name)), '')
-
else
-
return str
-
end
-
end
-
-
encoding = str.encoding
-
newlines = Regexp.new("\r\n|\r|\n".encode(encoding).force_encoding("binary"))
-
str.force_encoding("binary").split(newlines).each_with_index do |line, i|
-
begin
-
line.encode(encoding)
-
rescue Encoding::UndefinedConversionError => e
-
yield <<MSG.rstrip, i + 1
-
Invalid #{encoding.name} character #{e.error_char.dump}
-
MSG
-
end
-
end
-
return str
-
end
-
-
# Like {\#check\_encoding}, but also checks for a `@charset` declaration
-
# at the beginning of the file and uses that encoding if it exists.
-
#
-
# The Sass encoding rules are simple.
-
# If a `@charset` declaration exists,
-
# we assume that that's the original encoding of the document.
-
# Otherwise, we use whatever encoding Ruby has.
-
# Then we convert that to UTF-8 to process internally.
-
# The UTF-8 end result is what's returned by this method.
-
#
-
# @param str [String] The string of which to check the encoding
-
# @yield [msg] A block in which an encoding error can be raised.
-
# Only yields if there is an encoding error
-
# @yieldparam msg [String] The error message to be raised
-
# @return [(String, Encoding)] The original string encoded as UTF-8,
-
# and the source encoding of the string (or `nil` under Ruby 1.8)
-
# @raise [Encoding::UndefinedConversionError] if the source encoding
-
# cannot be converted to UTF-8
-
# @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
-
1
def check_sass_encoding(str, &block)
-
return check_encoding(str, &block), nil if ruby1_8?
-
# We allow any printable ASCII characters but double quotes in the charset decl
-
bin = str.dup.force_encoding("BINARY")
-
encoding = Sass::Util::ENCODINGS_TO_CHECK.find do |enc|
-
re = Sass::Util::CHARSET_REGEXPS[enc]
-
re && bin =~ re
-
end
-
charset, bom = $1, $2
-
if charset
-
charset = charset.force_encoding(encoding).encode("UTF-8")
-
if endianness = encoding[/[BL]E$/]
-
begin
-
Encoding.find(charset + endianness)
-
charset << endianness
-
rescue ArgumentError # Encoding charset + endianness doesn't exist
-
end
-
end
-
str.force_encoding(charset)
-
elsif bom
-
str.force_encoding(encoding)
-
end
-
-
str = check_encoding(str, &block)
-
return str.encode("UTF-8"), str.encoding
-
end
-
-
1
unless ruby1_8?
-
# @private
-
1
def _enc(string, encoding)
-
string.encode(encoding).force_encoding("BINARY")
-
end
-
-
# We could automatically add in any non-ASCII-compatible encodings here,
-
# but there's not really a good way to do that
-
# without manually checking that each encoding
-
# encodes all ASCII characters properly,
-
# which takes long enough to affect the startup time of the CLI.
-
1
ENCODINGS_TO_CHECK = %w[UTF-8 UTF-16BE UTF-16LE UTF-32BE UTF-32LE]
-
-
1
CHARSET_REGEXPS = Hash.new do |h, e|
-
h[e] =
-
begin
-
# /\A(?:\uFEFF)?@charset "(.*?)"|\A(\uFEFF)/
-
Regexp.new(/\A(?:#{_enc("\uFEFF", e)})?#{
-
_enc('@charset "', e)}(.*?)#{_enc('"', e)}|\A(#{
-
_enc("\uFEFF", e)})/)
-
rescue Encoding::ConverterNotFound => _
-
nil # JRuby on Java 5 doesn't support UTF-32
-
rescue
-
# /\A@charset "(.*?)"/
-
Regexp.new(/\A#{_enc('@charset "', e)}(.*?)#{_enc('"', e)}/)
-
end
-
end
-
end
-
-
# Checks to see if a class has a given method.
-
# For example:
-
#
-
# Sass::Util.has?(:public_instance_method, String, :gsub) #=> true
-
#
-
# Method collections like `Class#instance_methods`
-
# return strings in Ruby 1.8 and symbols in Ruby 1.9 and on,
-
# so this handles checking for them in a compatible way.
-
#
-
# @param attr [#to_s] The (singular) name of the method-collection method
-
# (e.g. `:instance_methods`, `:private_methods`)
-
# @param klass [Module] The class to check the methods of which to check
-
# @param method [String, Symbol] The name of the method do check for
-
# @return [Boolean] Whether or not the given collection has the given method
-
1
def has?(attr, klass, method)
-
1
klass.send("#{attr}s").include?(ruby1_8? ? method.to_s : method.to_sym)
-
end
-
-
# A version of `Enumerable#enum_with_index` that works in Ruby 1.8 and 1.9.
-
#
-
# @param enum [Enumerable] The enumerable to get the enumerator for
-
# @return [Enumerator] The with-index enumerator
-
1
def enum_with_index(enum)
-
ruby1_8? ? enum.enum_with_index : enum.each_with_index
-
end
-
-
# A version of `Enumerable#enum_cons` that works in Ruby 1.8 and 1.9.
-
#
-
# @param enum [Enumerable] The enumerable to get the enumerator for
-
# @param n [Fixnum] The size of each cons
-
# @return [Enumerator] The consed enumerator
-
1
def enum_cons(enum, n)
-
ruby1_8? ? enum.enum_cons(n) : enum.each_cons(n)
-
end
-
-
# A version of `Enumerable#enum_slice` that works in Ruby 1.8 and 1.9.
-
#
-
# @param enum [Enumerable] The enumerable to get the enumerator for
-
# @param n [Fixnum] The size of each slice
-
# @return [Enumerator] The consed enumerator
-
1
def enum_slice(enum, n)
-
ruby1_8? ? enum.enum_slice(n) : enum.each_slice(n)
-
end
-
-
# Destructively removes all elements from an array that match a block, and
-
# returns the removed elements.
-
#
-
# @param array [Array] The array from which to remove elements.
-
# @yield [el] Called for each element.
-
# @yieldparam el [*] The element to test.
-
# @yieldreturn [Boolean] Whether or not to extract the element.
-
# @return [Array] The extracted elements.
-
1
def extract!(array)
-
out = []
-
array.reject! do |e|
-
next false unless yield e
-
out << e
-
true
-
end
-
out
-
end
-
-
# Returns the ASCII code of the given character.
-
#
-
# @param c [String] All characters but the first are ignored.
-
# @return [Fixnum] The ASCII code of `c`.
-
1
def ord(c)
-
ruby1_8? ? c[0] : c.ord
-
end
-
-
# Flattens the first `n` nested arrays in a cross-version manner.
-
#
-
# @param arr [Array] The array to flatten
-
# @param n [Fixnum] The number of levels to flatten
-
# @return [Array] The flattened array
-
1
def flatten(arr, n)
-
return arr.flatten(n) unless ruby1_8_6?
-
return arr if n == 0
-
arr.inject([]) {|res, e| e.is_a?(Array) ? res.concat(flatten(e, n - 1)) : res << e}
-
end
-
-
# Returns the hash code for a set in a cross-version manner.
-
# Aggravatingly, this is order-dependent in Ruby 1.8.6.
-
#
-
# @param set [Set]
-
# @return [Fixnum] The order-independent hashcode of `set`
-
1
def set_hash(set)
-
return set.hash unless ruby1_8_6?
-
set.map {|e| e.hash}.uniq.sort.hash
-
end
-
-
# Tests the hash-equality of two sets in a cross-version manner.
-
# Aggravatingly, this is order-dependent in Ruby 1.8.6.
-
#
-
# @param set1 [Set]
-
# @param set2 [Set]
-
# @return [Boolean] Whether or not the sets are hashcode equal
-
1
def set_eql?(set1, set2)
-
return set1.eql?(set2) unless ruby1_8_6?
-
set1.to_a.uniq.sort_by {|e| e.hash}.eql?(set2.to_a.uniq.sort_by {|e| e.hash})
-
end
-
-
# Like `Object#inspect`, but preserves non-ASCII characters rather than escaping them under Ruby 1.9.2.
-
# This is necessary so that the precompiled Haml template can be `#encode`d into `@options[:encoding]`
-
# before being evaluated.
-
#
-
# @param obj {Object}
-
# @return {String}
-
1
def inspect_obj(obj)
-
return obj.inspect unless version_geq(::RUBY_VERSION, "1.9.2")
-
return ':' + inspect_obj(obj.to_s) if obj.is_a?(Symbol)
-
return obj.inspect unless obj.is_a?(String)
-
'"' + obj.gsub(/[\x00-\x7F]+/) {|s| s.inspect[1...-1]} + '"'
-
end
-
-
# Extracts the non-string vlaues from an array containing both strings and non-strings.
-
# These values are replaced with escape sequences.
-
# This can be undone using \{#inject\_values}.
-
#
-
# This is useful e.g. when we want to do string manipulation
-
# on an interpolated string.
-
#
-
# The precise format of the resulting string is not guaranteed.
-
# However, it is guaranteed that newlines and whitespace won't be affected.
-
#
-
# @param arr [Array] The array from which values are extracted.
-
# @return [(String, Array)] The resulting string, and an array of extracted values.
-
1
def extract_values(arr)
-
values = []
-
return arr.map do |e|
-
next e.gsub('{', '{{') if e.is_a?(String)
-
values << e
-
next "{#{values.count - 1}}"
-
end.join, values
-
end
-
-
# Undoes \{#extract\_values} by transforming a string with escape sequences
-
# into an array of strings and non-string values.
-
#
-
# @param str [String] The string with escape sequences.
-
# @param values [Array] The array of values to inject.
-
# @return [Array] The array of strings and values.
-
1
def inject_values(str, values)
-
return [str.gsub('{{', '{')] if values.empty?
-
# Add an extra { so that we process the tail end of the string
-
result = (str + '{{').scan(/(.*?)(?:(\{\{)|\{(\d+)\})/m).map do |(pre, esc, n)|
-
[pre, esc ? '{' : '', n ? values[n.to_i] : '']
-
end.flatten(1)
-
result[-2] = '' # Get rid of the extra {
-
merge_adjacent_strings(result).reject {|s| s == ''}
-
end
-
-
# Allows modifications to be performed on the string form
-
# of an array containing both strings and non-strings.
-
#
-
# @param arr [Array] The array from which values are extracted.
-
# @yield [str] A block in which string manipulation can be done to the array.
-
# @yieldparam str [String] The string form of `arr`.
-
# @yieldreturn [String] The modified string.
-
# @return [Array] The modified, interpolated array.
-
1
def with_extracted_values(arr)
-
str, vals = extract_values(arr)
-
str = yield str
-
inject_values(str, vals)
-
end
-
-
## Static Method Stuff
-
-
# The context in which the ERB for \{#def\_static\_method} will be run.
-
1
class StaticConditionalContext
-
# @param set [#include?] The set of variables that are defined for this context.
-
1
def initialize(set)
-
@set = set
-
end
-
-
# Checks whether or not a variable is defined for this context.
-
#
-
# @param name [Symbol] The name of the variable
-
# @return [Boolean]
-
1
def method_missing(name, *args, &block)
-
super unless args.empty? && block.nil?
-
@set.include?(name)
-
end
-
end
-
-
1
private
-
-
# Calculates the memoization table for the Least Common Subsequence algorithm.
-
# Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS)
-
1
def lcs_table(x, y)
-
c = Array.new(x.size) {[]}
-
x.size.times {|i| c[i][0] = 0}
-
y.size.times {|j| c[0][j] = 0}
-
(1...x.size).each do |i|
-
(1...y.size).each do |j|
-
c[i][j] =
-
if yield x[i], y[j]
-
c[i-1][j-1] + 1
-
else
-
[c[i][j-1], c[i-1][j]].max
-
end
-
end
-
end
-
return c
-
end
-
-
# Computes a single longest common subsequence for arrays x and y.
-
# Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS)
-
1
def lcs_backtrace(c, x, y, i, j, &block)
-
return [] if i == 0 || j == 0
-
if v = yield(x[i], y[j])
-
return lcs_backtrace(c, x, y, i-1, j-1, &block) << v
-
end
-
-
return lcs_backtrace(c, x, y, i, j-1, &block) if c[i][j-1] > c[i-1][j]
-
return lcs_backtrace(c, x, y, i-1, j, &block)
-
end
-
end
-
end
-
-
1
require 'sass/util/multibyte_string_scanner'
-
1
require 'strscan'
-
-
1
if Sass::Util.ruby1_8?
-
Sass::Util::MultibyteStringScanner = StringScanner
-
else
-
# A wrapper of the native StringScanner class that works correctly with
-
# multibyte character encodings. The native class deals only in bytes, not
-
# characters, for methods like [#pos] and [#matched_size]. This class deals
-
# only in characters, instead.
-
1
class Sass::Util::MultibyteStringScanner < StringScanner
-
1
def self.new(str)
-
return StringScanner.new(str) if str.ascii_only?
-
super
-
end
-
-
1
def initialize(str)
-
super
-
@mb_pos = 0
-
@mb_matched_size = nil
-
@mb_last_pos = nil
-
end
-
-
1
alias_method :byte_pos, :pos
-
1
alias_method :byte_matched_size, :matched_size
-
-
1
def check(pattern); _match super; end
-
1
def check_until(pattern); _matched super; end
-
1
def getch; _forward _match super; end
-
1
def match?(pattern); _size check(pattern); end
-
1
def matched_size; @mb_matched_size; end
-
1
def peek(len); string[@mb_pos, len]; end
-
1
alias_method :peep, :peek
-
1
def pos; @mb_pos; end
-
1
alias_method :pointer, :pos
-
1
def rest_size; rest.size; end
-
1
def scan(pattern); _forward _match super; end
-
1
def scan_until(pattern); _forward _matched super; end
-
1
def skip(pattern); _size scan(pattern); end
-
1
def skip_until(pattern); _matched _size scan_until(pattern); end
-
-
1
def get_byte
-
raise "MultibyteStringScanner doesn't support #get_byte."
-
end
-
-
1
def getbyte
-
raise "MultibyteStringScanner doesn't support #getbyte."
-
end
-
-
1
def pos=(n)
-
@mb_last_pos = nil
-
-
# We set position kind of a lot during parsing, so we want it to be as
-
# efficient as possible. This is complicated by the fact that UTF-8 is a
-
# variable-length encoding, so it's difficult to find the byte length that
-
# corresponds to a given character length.
-
#
-
# Our heuristic here is to try to count the fewest possible characters. So
-
# if the new position is close to the current one, just count the
-
# characters between the two; if the new position is closer to the
-
# beginning of the string, just count the characters from there.
-
if @mb_pos - n < @mb_pos / 2
-
# New position is close to old position
-
byte_delta = @mb_pos > n ? -string[n...@mb_pos].bytesize : string[@mb_pos...n].bytesize
-
super(byte_pos + byte_delta)
-
else
-
# New position is close to BOS
-
super(string[0...n].bytesize)
-
end
-
@mb_pos = n
-
end
-
-
1
def reset
-
@mb_pos = 0
-
@mb_matched_size = nil
-
@mb_last_pos = nil
-
super
-
end
-
-
1
def scan_full(pattern, advance_pointer_p, return_string_p)
-
res = _match super(pattern, advance_pointer_p, true)
-
_forward res if advance_pointer_p
-
return res if return_string_p
-
end
-
-
1
def search_full(pattern, advance_pointer_p, return_string_p)
-
res = super(pattern, advance_pointer_p, true)
-
_forward res if advance_pointer_p
-
_matched((res if return_string_p))
-
end
-
-
1
def string=(str)
-
@mb_pos = 0
-
@mb_matched_size = nil
-
@mb_last_pos = nil
-
super
-
end
-
-
1
def terminate
-
@mb_pos = string.size
-
@mb_matched_size = nil
-
@mb_last_pos = nil
-
super
-
end
-
1
alias_method :clear, :terminate
-
-
1
def unscan
-
super
-
@mb_pos = @mb_last_pos
-
@mb_last_pos = @mb_matched_size = nil
-
end
-
-
1
private
-
-
1
def _size(str)
-
str && str.size
-
end
-
-
1
def _match(str)
-
@mb_matched_size = str && str.size
-
str
-
end
-
-
1
def _matched(res)
-
_match matched
-
res
-
end
-
-
1
def _forward(str)
-
@mb_last_pos = @mb_pos
-
@mb_pos += str.size if str
-
str
-
end
-
end
-
end
-
1
require 'set'
-
-
1
module Sass
-
1
module Util
-
# A map from sets to values.
-
# A value is \{#\[]= set} by providing a set (the "set-set") and a value,
-
# which is then recorded as corresponding to that set.
-
# Values are \{#\[] accessed} by providing a set (the "get-set")
-
# and returning all values that correspond to set-sets
-
# that are subsets of the get-set.
-
#
-
# SubsetMap preserves the order of values as they're inserted.
-
#
-
# @example
-
# ssm = SubsetMap.new
-
# ssm[Set[1, 2]] = "Foo"
-
# ssm[Set[2, 3]] = "Bar"
-
# ssm[Set[1, 2, 3]] = "Baz"
-
#
-
# ssm[Set[1, 2, 3]] #=> ["Foo", "Bar", "Baz"]
-
1
class SubsetMap
-
# Creates a new, empty SubsetMap.
-
1
def initialize
-
@hash = {}
-
@vals = []
-
end
-
-
# Whether or not this SubsetMap has any key-value pairs.
-
#
-
# @return [Boolean]
-
1
def empty?
-
@hash.empty?
-
end
-
-
# Associates a value with a set.
-
# When `set` or any of its supersets is accessed,
-
# `value` will be among the values returned.
-
#
-
# Note that if the same `set` is passed to this method multiple times,
-
# all given `value`s will be associated with that `set`.
-
#
-
# This runs in `O(n)` time, where `n` is the size of `set`.
-
#
-
# @param set [#to_set] The set to use as the map key. May not be empty.
-
# @param value [Object] The value to associate with `set`.
-
# @raise [ArgumentError] If `set` is empty.
-
1
def []=(set, value)
-
raise ArgumentError.new("SubsetMap keys may not be empty.") if set.empty?
-
-
index = @vals.size
-
@vals << value
-
set.each do |k|
-
@hash[k] ||= []
-
@hash[k] << [set, set.to_set, index]
-
end
-
end
-
-
# Returns all values associated with subsets of `set`.
-
#
-
# In the worst case, this runs in `O(m*max(n, log m))` time,
-
# where `n` is the size of `set`
-
# and `m` is the number of assocations in the map.
-
# However, unless many keys in the map overlap with `set`,
-
# `m` will typically be much smaller.
-
#
-
# @param set [Set] The set to use as the map key.
-
# @return [Array<(Object, #to_set)>] An array of pairs,
-
# where the first value is the value associated with a subset of `set`,
-
# and the second value is that subset of `set`
-
# (or whatever `#to_set` object was used to set the value)
-
# This array is in insertion order.
-
# @see #[]
-
1
def get(set)
-
res = set.map do |k|
-
next unless subsets = @hash[k]
-
subsets.map do |subenum, subset, index|
-
next unless subset.subset?(set)
-
[index, subenum]
-
end
-
end
-
res = Sass::Util.flatten(res, 1)
-
res.compact!
-
res.uniq!
-
res.sort!
-
res.map! {|i, s| [@vals[i], s]}
-
return res
-
end
-
-
# Same as \{#get}, but doesn't return the subsets of the argument
-
# for which values were found.
-
#
-
# @param set [Set] The set to use as the map key.
-
# @return [Array] The array of all values
-
# associated with subsets of `set`, in insertion order.
-
# @see #get
-
1
def [](set)
-
get(set).map {|v, _| v}
-
end
-
end
-
end
-
end
-
# This is necessary for loading Sass when Haml is required in Rails 3.
-
# Once the split is complete, we can remove it.
-
1
require File.dirname(__FILE__) + '/../sass'
-
1
require 'sass/util'
-
-
1
module Sass
-
# Handles Sass version-reporting.
-
# Sass not only reports the standard three version numbers,
-
# but its Git revision hash as well,
-
# if it was installed from Git.
-
1
module Version
-
1
include Sass::Util
-
-
# Returns a hash representing the version of Sass.
-
# The `:major`, `:minor`, and `:teeny` keys have their respective numbers as Fixnums.
-
# The `:name` key has the name of the version.
-
# The `:string` key contains a human-readable string representation of the version.
-
# The `:number` key is the major, minor, and teeny keys separated by periods.
-
# If Sass is checked out from Git, the `:rev` key will have the revision hash.
-
# For example:
-
#
-
# {
-
# :string => "2.1.0.9616393",
-
# :rev => "9616393b8924ef36639c7e82aa88a51a24d16949",
-
# :number => "2.1.0",
-
# :major => 2, :minor => 1, :teeny => 0
-
# }
-
#
-
# If a prerelease version of Sass is being used,
-
# the `:string` and `:number` fields will reflect the full version
-
# (e.g. `"2.2.beta.1"`), and the `:teeny` field will be `-1`.
-
# A `:prerelease` key will contain the name of the prerelease (e.g. `"beta"`),
-
# and a `:prerelease_number` key will contain the rerelease number.
-
# For example:
-
#
-
# {
-
# :string => "3.0.beta.1",
-
# :number => "3.0.beta.1",
-
# :major => 3, :minor => 0, :teeny => -1,
-
# :prerelease => "beta",
-
# :prerelease_number => 1
-
# }
-
#
-
# @return [{Symbol => String/Fixnum}] The version hash
-
1
def version
-
1
return @@version if defined?(@@version)
-
-
1
numbers = File.read(scope('VERSION')).strip.split('.').
-
3
map {|n| n =~ /^[0-9]+$/ ? n.to_i : n}
-
1
name = File.read(scope('VERSION_NAME')).strip
-
1
@@version = {
-
:major => numbers[0],
-
:minor => numbers[1],
-
:teeny => numbers[2],
-
:name => name
-
}
-
-
1
if numbers[3].is_a?(String)
-
@@version[:teeny] = -1
-
@@version[:prerelease] = numbers[3]
-
@@version[:prerelease_number] = numbers[4]
-
end
-
-
1
@@version[:number] = numbers.join('.')
-
1
@@version[:string] = @@version[:number].dup
-
-
1
if rev = revision_number
-
@@version[:rev] = rev
-
unless rev[0] == ?(
-
@@version[:string] << "." << rev[0...7]
-
end
-
end
-
-
1
@@version[:string] << " (#{name})"
-
1
@@version
-
end
-
-
1
private
-
-
1
def revision_number
-
1
if File.exists?(scope('REVISION'))
-
1
rev = File.read(scope('REVISION')).strip
-
1
return rev unless rev =~ /^([a-f0-9]+|\(.*\))$/ || rev == '(unknown)'
-
end
-
-
1
return unless File.exists?(scope('.git/HEAD'))
-
rev = File.read(scope('.git/HEAD')).strip
-
return rev unless rev =~ /^ref: (.*)$/
-
-
ref_name = $1
-
ref_file = scope(".git/#{ref_name}")
-
info_file = scope(".git/info/refs")
-
return File.read(ref_file).strip if File.exists?(ref_file)
-
return unless File.exists?(info_file)
-
File.open(info_file) do |f|
-
f.each do |l|
-
sha, ref = l.strip.split("\t", 2)
-
next unless ref == ref_name
-
return sha
-
end
-
end
-
return nil
-
end
-
end
-
-
1
extend Sass::Version
-
-
# A string representing the version of Sass.
-
# A more fine-grained representation is available from Sass.version.
-
# @api public
-
1
VERSION = version[:string] unless defined?(Sass::VERSION)
-
end
-
1
require 'sass/rails'
-
1
module Sass
-
1
module Rails
-
end
-
end
-
-
1
require 'sass/rails/compressor'
-
1
require 'sass/rails/logger'
-
1
require 'sass/rails/railtie'
-
1
require 'sass/rails/helpers'
-
1
require 'sass/rails/importer'
-
1
require 'sass/rails/template_handlers'
-
1
require 'sass/rails/version'
-
1
require 'sass'
-
-
1
module Sass
-
1
module Rails
-
1
class CssCompressor
-
1
def compress(css)
-
if css.count("\n") > 2
-
Sass::Engine.new(css,
-
:syntax => :scss,
-
:cache => false,
-
:read_cache => false,
-
:style => :compressed).render
-
else
-
css
-
end
-
end
-
end
-
end
-
end
-
1
module Sass
-
1
module Rails
-
1
module Helpers
-
-
1
def asset_data_url(path)
-
data = context_asset_data_uri(path.value)
-
Sass::Script::String.new(%Q{url(#{data})})
-
end
-
-
1
def asset_path(asset, kind)
-
Sass::Script::String.new(public_path(asset.value, kind.value), true)
-
end
-
-
1
def asset_url(asset, kind)
-
Sass::Script::String.new(%Q{url(#{public_path(asset.value, kind.value)})})
-
end
-
-
1
[:image, :video, :audio, :javascript, :stylesheet].each do |asset_class|
-
class_eval %Q{
-
def #{asset_class}_path(asset)
-
Sass::Script::String.new(resolver.#{asset_class}_path(asset.value), true)
-
end
-
def #{asset_class}_url(asset)
-
Sass::Script::String.new("url(" + resolver.#{asset_class}_path(asset.value) + ")")
-
end
-
5
}, __FILE__, __LINE__ - 6
-
end
-
-
1
def font_path(asset)
-
asset_path(asset, Sass::Script::String.new("font"))
-
end
-
-
1
def font_url(asset)
-
asset_url(asset, Sass::Script::String.new("font"))
-
end
-
-
1
protected
-
-
1
def resolver
-
options[:custom][:resolver]
-
end
-
-
1
def public_path(asset, kind)
-
resolver.public_path(asset, kind.pluralize)
-
end
-
-
1
def context_asset_data_uri(path)
-
resolver.context.asset_data_uri(path)
-
end
-
end
-
end
-
end
-
-
1
module Sass
-
1
module Script
-
1
module Functions
-
1
include Sass::Rails::Helpers
-
end
-
end
-
end
-
1
require 'sprockets'
-
-
1
module Sass::Rails
-
1
class Importer
-
1
GLOB = /\*|\[.+\]/
-
1
PARTIAL = /^_/
-
1
HAS_EXTENSION = /\.css(.s[ac]ss)?$/
-
-
1
SASS_EXTENSIONS = {
-
".css.sass" => :sass,
-
".css.scss" => :scss,
-
".sass" => :sass,
-
".scss" => :scss
-
}
-
1
attr_reader :context
-
-
1
def initialize(context)
-
@context = context
-
@resolver = Resolver.new(context)
-
end
-
-
1
def sass_file?(filename)
-
filename = filename.to_s
-
SASS_EXTENSIONS.keys.any?{|ext| filename[ext]}
-
end
-
-
1
def syntax(filename)
-
filename = filename.to_s
-
SASS_EXTENSIONS.each {|ext, syntax| return syntax if filename[(ext.size+2)..-1][ext]}
-
nil
-
end
-
-
1
def resolve(name, base_pathname = nil)
-
name = Pathname.new(name)
-
if base_pathname && base_pathname.to_s.size > 0
-
root = Pathname.new(context.root_path)
-
name = base_pathname.relative_path_from(root).join(name)
-
end
-
partial_name = name.dirname.join("_#{name.basename}")
-
@resolver.resolve(name) || @resolver.resolve(partial_name)
-
end
-
-
1
def find_relative(name, base, options)
-
base_pathname = Pathname.new(base)
-
if name =~ GLOB
-
glob_imports(name, base_pathname, options)
-
elsif pathname = resolve(name, base_pathname.dirname)
-
context.depend_on(pathname)
-
if sass_file?(pathname)
-
Sass::Engine.new(pathname.read, options.merge(:filename => pathname.to_s, :importer => self, :syntax => syntax(pathname)))
-
else
-
Sass::Engine.new(@resolver.process(pathname), options.merge(:filename => pathname.to_s, :importer => self, :syntax => :scss))
-
end
-
else
-
nil
-
end
-
end
-
-
1
def find(name, options)
-
if name =~ GLOB
-
nil # globs must be relative
-
elsif pathname = resolve(name)
-
context.depend_on(pathname)
-
if sass_file?(pathname)
-
Sass::Engine.new(pathname.read, options.merge(:filename => pathname.to_s, :importer => self, :syntax => syntax(pathname)))
-
else
-
Sass::Engine.new(@resolver.process(pathname), options.merge(:filename => pathname.to_s, :importer => self, :syntax => :scss))
-
end
-
else
-
nil
-
end
-
end
-
-
1
def each_globbed_file(glob, base_pathname, options)
-
Dir["#{base_pathname}/#{glob}"].sort.each do |filename|
-
next if filename == options[:filename]
-
yield filename if File.directory?(filename) || context.asset_requirable?(filename)
-
end
-
end
-
-
1
def glob_imports(glob, base_pathname, options)
-
contents = ""
-
each_globbed_file(glob, base_pathname.dirname, options) do |filename|
-
if File.directory?(filename)
-
context.depend_on(filename)
-
elsif context.asset_requirable?(filename)
-
context.depend_on(filename)
-
contents << "@import #{Pathname.new(filename).relative_path_from(base_pathname.dirname).to_s.inspect};\n"
-
end
-
end
-
return nil if contents.empty?
-
Sass::Engine.new(contents, options.merge(
-
:filename => base_pathname.to_s,
-
:importer => self,
-
:syntax => :scss
-
))
-
end
-
-
1
def mtime(name, options)
-
if name =~ GLOB && options[:filename]
-
mtime = nil
-
each_globbed_file(name, Pathname.new(options[:filename]).dirname, options) do |p|
-
mtime ||= File.mtime(p)
-
mtime = [mtime, File.mtime(p)].max
-
end
-
mtime
-
elsif pathname = resolve(name)
-
pathname.mtime
-
end
-
end
-
-
1
def key(name, options)
-
["Sprockets:" + File.dirname(File.expand_path(name)), File.basename(name)]
-
end
-
-
1
def to_s
-
"Sass::Rails::Importer(#{context.pathname})"
-
end
-
-
end
-
-
end
-
1
require 'sass/logger'
-
-
1
module Sass
-
1
module Rails
-
1
class Logger < Sass::Logger::Base
-
1
def _log(level, message)
-
-
case level
-
when :trace, :debug
-
::Rails.logger.debug message
-
when :warn
-
::Rails.logger.warn message
-
when :error
-
::Rails.logger.error message
-
when :info
-
::Rails.logger.info message
-
end
-
end
-
end
-
end
-
end
-
1
require 'sprockets/railtie'
-
-
1
module Sass::Rails
-
1
class Railtie < ::Rails::Railtie
-
1
module SassContext
-
1
attr_accessor :sass_config
-
end
-
-
1
config.sass = ActiveSupport::OrderedOptions.new
-
-
# Establish static configuration defaults
-
# Emit scss files during stylesheet generation of scaffold
-
1
config.sass.preferred_syntax = :scss
-
# Use expanded output instead of the sass default of :nested
-
1
config.sass.style = :expanded
-
# Write sass cache files for performance
-
1
config.sass.cache = true
-
# Read sass cache files for performance
-
1
config.sass.read_cache = true
-
# Display line comments above each selector as a debugging aid
-
1
config.sass.line_comments = true
-
# Initialize the load paths to an empty array
-
1
config.sass.load_paths = []
-
# Send Sass logs to Rails.logger
-
1
config.sass.logger = Sass::Rails::Logger.new
-
-
# Set the default stylesheet engine
-
# It can be overridedden by passing:
-
# --stylesheet_engine=sass
-
# to the rails generate command
-
1
config.app_generators.stylesheet_engine config.sass.preferred_syntax
-
-
1
config.before_initialize do
-
1
require 'sass'
-
-
# Assume dependency on sprockets, not on app.config.assets enabled.
-
1
if defined?(Sprockets::Engines)
-
1
Sprockets::Engines #force autoloading
-
1
Sprockets.register_engine '.sass', Sass::Rails::SassTemplate
-
1
Sprockets.register_engine '.scss', Sass::Rails::ScssTemplate
-
end
-
end
-
-
# Remove the sass middleware if it gets inadvertently enabled by applications.
-
1
config.after_initialize do |app|
-
1
app.config.middleware.delete(Sass::Plugin::Rack) if defined?(Sass::Plugin::Rack)
-
end
-
-
1
initializer :setup_sass, :group => :all do |app|
-
# Only emit one kind of syntax because though we have registered two kinds of generators
-
1
syntax = app.config.sass.preferred_syntax.to_sym
-
1
alt_syntax = syntax == :sass ? "scss" : "sass"
-
1
app.config.generators.hide_namespace alt_syntax
-
-
# Override stylesheet engine to the preferred syntax
-
1
config.app_generators.stylesheet_engine syntax
-
-
# Set the sass cache location
-
1
config.sass.cache_location = File.join(Rails.root, "tmp/cache/sass")
-
-
# Establish configuration defaults that are evironmental in nature
-
1
if config.sass.full_exception.nil?
-
# Display a stack trace in the css output when in development-like environments.
-
1
config.sass.full_exception = app.config.consider_all_requests_local
-
end
-
1
app.assets.context_class.extend(SassContext)
-
1
app.assets.context_class.sass_config = app.config.sass
-
-
1
Sass.logger = app.config.sass.logger
-
end
-
-
1
initializer :setup_compression, :group => :all do |app|
-
1
if app.config.assets.compress
-
app.config.sass.style = :compressed
-
app.config.assets.css_compressor = CssCompressor.new
-
end
-
end
-
end
-
end
-
1
require 'tilt'
-
1
require 'sprockets'
-
-
1
module Sass::Rails
-
-
1
class Resolver
-
-
1
attr_accessor :context
-
-
1
def initialize(context)
-
@context = context
-
end
-
-
1
def resolve(path, content_type = :self)
-
options = {}
-
options[:content_type] = content_type unless content_type.nil?
-
context.resolve(path, options)
-
rescue Sprockets::FileNotFound, Sprockets::ContentTypeMismatch
-
nil
-
end
-
-
1
def public_path(path, scope = nil)
-
context.asset_paths.compute_public_path(path, ::Rails.application.config.assets.prefix)
-
end
-
-
1
def process(path)
-
context.environment[path].to_s
-
end
-
-
1
def image_path(img)
-
context.image_path(img)
-
end
-
-
1
def video_path(video)
-
context.video_path(video)
-
end
-
-
1
def audio_path(audio)
-
context.audio_path(audio)
-
end
-
-
1
def javascript_path(javascript)
-
context.javascript_path(javascript)
-
end
-
-
1
def stylesheet_path(stylesheet)
-
context.stylesheet_path(stylesheet)
-
end
-
end
-
-
1
class SassTemplate < Tilt::SassTemplate
-
1
self.default_mime_type = 'text/css'
-
-
1
def self.engine_initialized?
-
defined?(::Sass::Engine)
-
end
-
-
1
def initialize_engine
-
require_template_library 'sass'
-
end
-
-
1
def syntax
-
:sass
-
end
-
-
1
def sass_options_from_rails(scope)
-
scope.environment.context_class.sass_config
-
end
-
-
1
def sass_options(scope)
-
importer = self.importer(scope)
-
options = sass_options_from_rails(scope)
-
load_paths = (options[:load_paths] || []).dup
-
load_paths.unshift(importer)
-
resolver = Resolver.new(scope)
-
css_filename = File.join(::Rails.public_path, resolver.public_path(scope.logical_path)) + ".css"
-
options.merge(
-
:filename => eval_file,
-
:css_filename => css_filename,
-
:line => line,
-
:syntax => syntax,
-
:importer => importer,
-
:load_paths => load_paths,
-
:custom => {
-
:resolver => resolver
-
}
-
)
-
end
-
-
1
def importer(scope)
-
Sass::Rails::Importer.new(scope)
-
end
-
-
1
def prepare
-
end
-
-
1
def evaluate(scope, locals, &block)
-
Sass::Engine.new(data, sass_options(scope)).render
-
end
-
end
-
-
1
class ScssTemplate < SassTemplate
-
1
self.default_mime_type = 'text/css'
-
-
1
def syntax
-
:scss
-
end
-
end
-
end
-
1
module Sass
-
1
module Rails
-
1
VERSION = "3.1.6"
-
end
-
end
-
1
require 'sprockets/version'
-
-
1
module Sprockets
-
1
autoload :ArgumentError, "sprockets/errors"
-
1
autoload :Asset, "sprockets/asset"
-
1
autoload :AssetAttributes, "sprockets/asset_attributes"
-
1
autoload :BundledAsset, "sprockets/bundled_asset"
-
1
autoload :CharsetNormalizer, "sprockets/charset_normalizer"
-
1
autoload :CircularDependencyError, "sprockets/errors"
-
1
autoload :ContentTypeMismatch, "sprockets/errors"
-
1
autoload :Context, "sprockets/context"
-
1
autoload :DirectiveProcessor, "sprockets/directive_processor"
-
1
autoload :EcoTemplate, "sprockets/eco_template"
-
1
autoload :EjsTemplate, "sprockets/ejs_template"
-
1
autoload :EngineError, "sprockets/errors"
-
1
autoload :Engines, "sprockets/engines"
-
1
autoload :Environment, "sprockets/environment"
-
1
autoload :Error, "sprockets/errors"
-
1
autoload :FileNotFound, "sprockets/errors"
-
1
autoload :Index, "sprockets/index"
-
1
autoload :JstProcessor, "sprockets/jst_processor"
-
1
autoload :Processing, "sprockets/processing"
-
1
autoload :Processor, "sprockets/processor"
-
1
autoload :Server, "sprockets/server"
-
1
autoload :StaticAsset, "sprockets/static_asset"
-
1
autoload :Utils, "sprockets/utils"
-
-
1
module Cache
-
1
autoload :FileStore, "sprockets/cache/file_store"
-
end
-
end
-
1
require 'time'
-
-
1
module Sprockets
-
# `Asset` is the base class for `BundledAsset` and `StaticAsset`.
-
1
class Asset
-
# Internal initializer to load `Asset` from serialized `Hash`.
-
1
def self.from_hash(environment, hash)
-
asset = allocate
-
asset.init_with(environment, hash)
-
asset
-
end
-
-
# Define base set of attributes to be serialized.
-
1
def self.serialized_attributes
-
%w( id logical_path pathname )
-
end
-
-
1
attr_reader :environment
-
1
attr_reader :id, :logical_path, :pathname
-
-
1
def initialize(environment, logical_path, pathname)
-
@environment = environment
-
@logical_path = logical_path.to_s
-
@pathname = Pathname.new(pathname)
-
@id = environment.digest.update(object_id.to_s).to_s
-
end
-
-
# Initialize `Asset` from serialized `Hash`.
-
1
def init_with(environment, coder)
-
@environment = environment
-
@pathname = @mtime = @length = nil
-
-
self.class.serialized_attributes.each do |attr|
-
instance_variable_set("@#{attr}", coder[attr].to_s) if coder[attr]
-
end
-
-
if @pathname && @pathname.is_a?(String)
-
# Expand `$root` placeholder and wrapper string in a `Pathname`
-
@pathname = Pathname.new(expand_root_path(@pathname))
-
end
-
-
if @mtime && @mtime.is_a?(String)
-
# Parse time string
-
@mtime = Time.parse(@mtime)
-
end
-
-
if @length && @length.is_a?(String)
-
# Convert length to an `Integer`
-
@length = Integer(@length)
-
end
-
end
-
-
# Copy serialized attributes to the coder object
-
1
def encode_with(coder)
-
coder['class'] = self.class.name.sub(/Sprockets::/, '')
-
-
self.class.serialized_attributes.each do |attr|
-
value = send(attr)
-
coder[attr] = case value
-
when Time
-
value.iso8601
-
else
-
value.to_s
-
end
-
end
-
-
coder['pathname'] = relativize_root_path(coder['pathname'])
-
end
-
-
# Returns `Content-Type` from pathname.
-
1
def content_type
-
@content_type ||= environment.content_type_of(pathname)
-
end
-
-
# Get mtime at the time the `Asset` is built.
-
1
def mtime
-
@mtime ||= environment.stat(pathname).mtime
-
end
-
-
# Get length at the time the `Asset` is built.
-
1
def length
-
@length ||= environment.stat(pathname).size
-
end
-
-
# Get content digest at the time the `Asset` is built.
-
1
def digest
-
@digest ||= environment.file_digest(pathname).hexdigest
-
end
-
-
# Return logical path with digest spliced in.
-
#
-
# "foo/bar-37b51d194a7513e45b56f6524f2d51f2.js"
-
#
-
1
def digest_path
-
environment.attributes_for(logical_path).path_with_fingerprint(digest)
-
end
-
-
# Return an `Array` of `Asset` files that are declared dependencies.
-
1
def dependencies
-
[]
-
end
-
-
# Expand asset into an `Array` of parts.
-
#
-
# Appending all of an assets body parts together should give you
-
# the asset's contents as a whole.
-
#
-
# This allows you to link to individual files for debugging
-
# purposes.
-
1
def to_a
-
[self]
-
end
-
-
# Add enumerator to allow `Asset` instances to be used as Rack
-
# compatible body objects.
-
1
def each
-
yield to_s
-
end
-
-
# Checks if Asset is fresh by comparing the actual mtime and
-
# digest to the inmemory model.
-
#
-
# Used to test if cached models need to be rebuilt.
-
#
-
# Subclass must override `fresh?` or `stale?`.
-
1
def fresh?
-
!stale?
-
end
-
-
# Checks if Asset is stale by comparing the actual mtime and
-
# digest to the inmemory model.
-
#
-
# Subclass must override `fresh?` or `stale?`.
-
1
def stale?
-
!fresh?
-
end
-
-
# Pretty inspect
-
1
def inspect
-
"#<#{self.class}:0x#{object_id.to_s(16)} " +
-
"pathname=#{pathname.to_s.inspect}, " +
-
"mtime=#{mtime.inspect}, " +
-
"digest=#{digest.inspect}" +
-
">"
-
end
-
-
# Assets are equal if they share the same path, mtime and digest.
-
1
def eql?(other)
-
other.class == self.class &&
-
other.relative_pathname == self.relative_pathname &&
-
other.mtime.to_i == self.mtime.to_i &&
-
other.digest == self.digest
-
end
-
1
alias_method :==, :eql?
-
-
1
protected
-
# Get pathname with its root stripped.
-
1
def relative_pathname
-
Pathname.new(relativize_root_path(pathname))
-
end
-
-
# Replace `$root` placeholder with actual environment root.
-
1
def expand_root_path(path)
-
environment.attributes_for(path).expand_root
-
end
-
-
# Replace actual environment root with `$root` placeholder.
-
1
def relativize_root_path(path)
-
environment.attributes_for(path).relativize_root
-
end
-
-
# Check if dependency is fresh.
-
#
-
# `dep` is a `Hash` with `path`, `mtime` and `hexdigest` keys.
-
#
-
# A `Hash` is used rather than other `Asset` object because we
-
# want to test non-asset files and directories.
-
1
def dependency_fresh?(dep = {})
-
path, mtime, hexdigest = dep.values_at('path', 'mtime', 'hexdigest')
-
-
stat = environment.stat(path)
-
-
# If path no longer exists, its definitely stale.
-
if stat.nil?
-
return false
-
end
-
-
# Compare dependency mime to the actual mtime. If the
-
# dependency mtime is newer than the actual mtime, the file
-
# hasn't changed since we created this `Asset` instance.
-
#
-
# However, if the mtime is newer it doesn't mean the asset is
-
# stale. Many deployment environments may recopy or recheckout
-
# assets on each deploy. In this case the mtime would be the
-
# time of deploy rather than modified time.
-
if mtime >= stat.mtime
-
return true
-
end
-
-
digest = environment.file_digest(path)
-
-
# If the mtime is newer, do a full digest comparsion. Return
-
# fresh if the digests match.
-
if hexdigest == digest.hexdigest
-
return true
-
end
-
-
# Otherwise, its stale.
-
false
-
end
-
end
-
end
-
1
require 'pathname'
-
-
1
module Sprockets
-
# `AssetAttributes` is a wrapper similar to `Pathname` that provides
-
# some helper accessors.
-
#
-
# These methods should be considered internalish.
-
1
class AssetAttributes
-
1
attr_reader :environment, :pathname
-
-
1
def initialize(environment, path)
-
@environment = environment
-
@pathname = path.is_a?(Pathname) ? path : Pathname.new(path.to_s)
-
end
-
-
# Replaces `$root` placeholder with actual environment root.
-
1
def expand_root
-
pathname.to_s.sub(/^\$root/, environment.root)
-
end
-
-
# Replaces environment root with `$root` placeholder.
-
1
def relativize_root
-
pathname.to_s.sub(/^#{Regexp.escape(environment.root)}/, '$root')
-
end
-
-
# Returns paths search the load path for.
-
1
def search_paths
-
paths = [pathname.to_s]
-
-
if pathname.basename(extensions.join).to_s != 'index'
-
path_without_extensions = extensions.inject(pathname) { |p, ext| p.sub(ext, '') }
-
index_path = path_without_extensions.join("index#{extensions.join}").to_s
-
paths << index_path
-
end
-
-
paths
-
end
-
-
# Reverse guess logical path for fully expanded path.
-
#
-
# This has some known issues. For an example if a file is
-
# shaddowed in the path, but is required relatively, its logical
-
# path will be incorrect.
-
1
def logical_path
-
raise ArgumentError unless pathname.absolute?
-
-
if root_path = environment.paths.detect { |path| pathname.to_s[path] }
-
path = pathname.relative_path_from(Pathname.new(root_path)).to_s
-
path = engine_extensions.inject(path) { |p, ext| p.sub(ext, '') }
-
path = "#{path}#{engine_format_extension}" unless format_extension
-
path
-
else
-
raise FileOutsidePaths, "#{pathname} isn't in paths: #{environment.paths.join(', ')}"
-
end
-
end
-
-
# Returns `Array` of extension `String`s.
-
#
-
# "foo.js.coffee"
-
# # => [".js", ".coffee"]
-
#
-
1
def extensions
-
@extensions ||= @pathname.basename.to_s.scan(/\.[^.]+/)
-
end
-
-
# Returns the format extension.
-
#
-
# "foo.js.coffee"
-
# # => ".js"
-
#
-
1
def format_extension
-
extensions.reverse.detect { |ext|
-
@environment.mime_types(ext) && !@environment.engines(ext)
-
}
-
end
-
-
# Returns an `Array` of engine extensions.
-
#
-
# "foo.js.coffee.erb"
-
# # => [".coffee", ".erb"]
-
#
-
1
def engine_extensions
-
exts = extensions
-
-
if offset = extensions.index(format_extension)
-
exts = extensions[offset+1..-1]
-
end
-
-
exts.select { |ext| @environment.engines(ext) }
-
end
-
-
# Returns engine classes.
-
1
def engines
-
engine_extensions.map { |ext| @environment.engines(ext) }
-
end
-
-
# Returns all processors to run on the path.
-
1
def processors
-
environment.preprocessors(content_type) +
-
engines.reverse +
-
environment.postprocessors(content_type)
-
end
-
-
# Returns the content type for the pathname. Falls back to `application/octet-stream`.
-
1
def content_type
-
@content_type ||= begin
-
if format_extension.nil?
-
engine_content_type || 'application/octet-stream'
-
else
-
@environment.mime_types(format_extension) ||
-
engine_content_type ||
-
'application/octet-stream'
-
end
-
end
-
end
-
-
# Gets digest fingerprint.
-
#
-
# "foo-0aa2105d29558f3eb790d411d7d8fb66.js"
-
# # => "0aa2105d29558f3eb790d411d7d8fb66"
-
#
-
1
def path_fingerprint
-
pathname.basename(extensions.join).to_s =~ /-([0-9a-f]{7,40})$/ ? $1 : nil
-
end
-
-
# Injects digest fingerprint into path.
-
#
-
# "foo.js"
-
# # => "foo-0aa2105d29558f3eb790d411d7d8fb66.js"
-
#
-
1
def path_with_fingerprint(digest)
-
if old_digest = path_fingerprint
-
pathname.sub(old_digest, digest).to_s
-
else
-
pathname.to_s.sub(/\.(\w+)$/) { |ext| "-#{digest}#{ext}" }
-
end
-
end
-
-
1
private
-
# Returns implicit engine content type.
-
#
-
# `.coffee` files carry an implicit `application/javascript`
-
# content type.
-
1
def engine_content_type
-
engines.reverse.each do |engine|
-
if engine.respond_to?(:default_mime_type) && engine.default_mime_type
-
return engine.default_mime_type
-
end
-
end
-
nil
-
end
-
-
1
def engine_format_extension
-
if content_type = engine_content_type
-
environment.extension_for_mime_type(content_type)
-
end
-
end
-
end
-
end
-
1
require 'sprockets/asset_attributes'
-
1
require 'sprockets/bundled_asset'
-
1
require 'sprockets/caching'
-
1
require 'sprockets/digest'
-
1
require 'sprockets/processing'
-
1
require 'sprockets/server'
-
1
require 'sprockets/static_asset'
-
1
require 'sprockets/trail'
-
1
require 'pathname'
-
-
1
module Sprockets
-
# `Base` class for `Environment` and `Index`.
-
1
class Base
-
1
include Digest
-
1
include Caching, Processing, Server, Trail
-
-
# Get and set `Logger` instance.
-
1
attr_accessor :logger
-
-
# Get `Context` class.
-
#
-
# This class maybe mutated and mixed in with custom helpers.
-
#
-
# environment.context_class.instance_eval do
-
# include MyHelpers
-
# def asset_url; end
-
# end
-
#
-
1
attr_reader :context_class
-
-
# Get persistent cache store
-
1
attr_reader :cache
-
-
# Set persistent cache store
-
#
-
# The cache store must implement a pair of getters and
-
# setters. Either `get(key)`/`set(key, value)`,
-
# `[key]`/`[key]=value`, `read(key)`/`write(key, value)`.
-
1
def cache=(cache)
-
1
expire_index!
-
1
@cache = cache
-
end
-
-
# Return an `Index`. Must be implemented by the subclass.
-
1
def index
-
raise NotImplementedError
-
end
-
-
# Works like `Dir.entries`.
-
#
-
# Subclasses may cache this method.
-
1
def entries(pathname)
-
trail.entries(pathname)
-
end
-
-
# Works like `File.stat`.
-
#
-
# Subclasses may cache this method.
-
1
def stat(path)
-
trail.stat(path)
-
end
-
-
# Read and compute digest of filename.
-
#
-
# Subclasses may cache this method.
-
1
def file_digest(path, data = nil)
-
if stat = self.stat(path)
-
# `data` maybe provided
-
if data
-
digest.update(data)
-
-
# If its a file, digest the contents
-
elsif stat.file?
-
digest.file(path.to_s)
-
-
# If its a directive, digest the list of filenames
-
elsif stat.directory?
-
contents = self.entries(path).join(',')
-
digest.update(contents)
-
end
-
end
-
end
-
-
# Internal. Return a `AssetAttributes` for `path`.
-
1
def attributes_for(path)
-
AssetAttributes.new(self, path)
-
end
-
-
# Internal. Return content type of `path`.
-
1
def content_type_of(path)
-
attributes_for(path).content_type
-
end
-
-
# Find asset by logical path or expanded path.
-
1
def find_asset(path, options = {})
-
pathname = Pathname.new(path)
-
-
if pathname.absolute?
-
build_asset(attributes_for(pathname).logical_path, pathname, options)
-
else
-
find_asset_in_path(pathname, options)
-
end
-
end
-
-
# Preferred `find_asset` shorthand.
-
#
-
# environment['application.js']
-
#
-
1
def [](*args)
-
find_asset(*args)
-
end
-
-
1
def each_entry(root, &block)
-
return to_enum(__method__, root) unless block_given?
-
root = Pathname.new(root) unless root.is_a?(Pathname)
-
-
paths = []
-
entries(root).sort.each do |filename|
-
path = root.join(filename)
-
paths << path
-
-
if stat(path).directory?
-
each_entry(path) do |subpath|
-
paths << subpath
-
end
-
end
-
end
-
-
paths.sort_by(&:to_s).each(&block)
-
-
nil
-
end
-
-
1
def each_file
-
return to_enum(__method__) unless block_given?
-
paths.each do |root|
-
each_entry(root) do |path|
-
if !stat(path).directory?
-
yield path
-
end
-
end
-
end
-
nil
-
end
-
-
1
def each_logical_path
-
return to_enum(__method__) unless block_given?
-
files = {}
-
each_file do |filename|
-
logical_path = attributes_for(filename).logical_path
-
yield logical_path unless files[logical_path]
-
files[logical_path] = true
-
end
-
nil
-
end
-
-
# Pretty inspect
-
1
def inspect
-
"#<#{self.class}:0x#{object_id.to_s(16)} " +
-
"root=#{root.to_s.inspect}, " +
-
"paths=#{paths.inspect}, " +
-
"digest=#{digest.to_s.inspect}" +
-
">"
-
end
-
-
1
protected
-
# Clear index after mutating state. Must be implemented by the subclass.
-
1
def expire_index!
-
raise NotImplementedError
-
end
-
-
1
def build_asset(logical_path, pathname, options)
-
pathname = Pathname.new(pathname)
-
-
return unless stat(pathname)
-
-
# If there are any processors to run on the pathname, use
-
# `BundledAsset`. Otherwise use `StaticAsset` and treat is as binary.
-
if attributes_for(pathname).processors.any?
-
BundledAsset.new(self, logical_path, pathname, options)
-
else
-
StaticAsset.new(self, logical_path, pathname)
-
end
-
end
-
end
-
end
-
1
require 'sprockets/asset'
-
1
require 'sprockets/errors'
-
1
require 'fileutils'
-
1
require 'set'
-
1
require 'zlib'
-
-
1
module Sprockets
-
# `BundledAsset`s are used for files that need to be processed and
-
# concatenated with other assets. Use for `.js` and `.css` files.
-
1
class BundledAsset < Asset
-
# Define extra attributes to be serialized.
-
1
def self.serialized_attributes
-
super + %w( content_type mtime )
-
end
-
-
1
def initialize(environment, logical_path, pathname, options)
-
super(environment, logical_path, pathname)
-
@options = options || {}
-
end
-
-
# Initialize `BundledAsset` from serialized `Hash`.
-
1
def init_with(environment, coder)
-
@options = {}
-
-
super
-
-
@body = coder['body']
-
@assets = coder['asset_paths'].map { |p|
-
p = expand_root_path(p)
-
p == pathname.to_s ? self : environment[p, @options]
-
}
-
-
@dependency_paths = coder['dependency_paths'].map { |h|
-
h.merge('path' => expand_root_path(h['path']))
-
}
-
@dependency_paths.each do |dep|
-
dep['mtime'] = Time.parse(dep['mtime']) if dep['mtime'].is_a?(String)
-
end
-
end
-
-
# Serialize custom attributes in `BundledAsset`.
-
1
def encode_with(coder)
-
super
-
-
coder['body'] = body
-
coder['asset_paths'] = to_a.map { |a| relativize_root_path(a.pathname) }
-
coder['dependency_paths'] = dependency_paths.map { |h|
-
h.merge('path' => relativize_root_path(h['path']))
-
}
-
end
-
-
# Get asset's own processed contents. Excludes any of its required
-
# dependencies but does run any processors or engines on the
-
# original file.
-
1
def body
-
@body ||= build_dependency_context_and_body[1]
-
end
-
-
# Get latest mtime of all its dependencies.
-
1
def mtime
-
@mtime ||= dependency_paths.map { |h| h['mtime'] }.max
-
end
-
-
# Get size of concatenated source.
-
1
def length
-
@length ||= build_source['length']
-
end
-
-
# Compute digest of concatenated source.
-
1
def digest
-
@digest ||= build_source['digest']
-
end
-
-
# Return an `Array` of `Asset` files that are declared dependencies.
-
1
def dependencies
-
to_a - [self]
-
end
-
-
# Expand asset into an `Array` of parts.
-
1
def to_a
-
@assets ||= build_dependencies_paths_and_assets[1]
-
end
-
-
# Checks if Asset is stale by comparing the actual mtime and
-
# digest to the inmemory model.
-
1
def fresh?
-
# Check freshness of all declared dependencies
-
dependency_paths.all? { |h| dependency_fresh?(h) }
-
end
-
-
# Return `String` of concatenated source.
-
1
def to_s
-
@source ||= build_source['source']
-
end
-
-
# Save asset to disk.
-
1
def write_to(filename, options = {})
-
# Gzip contents if filename has '.gz'
-
options[:compress] ||= File.extname(filename) == '.gz'
-
-
File.open("#{filename}+", 'wb') do |f|
-
if options[:compress]
-
# Run contents through `Zlib`
-
gz = Zlib::GzipWriter.new(f, Zlib::BEST_COMPRESSION)
-
gz.write to_s
-
gz.close
-
else
-
# Write out as is
-
f.write to_s
-
f.close
-
end
-
end
-
-
# Atomic write
-
FileUtils.mv("#{filename}+", filename)
-
-
# Set mtime correctly
-
File.utime(mtime, mtime, filename)
-
-
nil
-
ensure
-
# Ensure tmp file gets cleaned up
-
FileUtils.rm("#{filename}+") if File.exist?("#{filename}+")
-
end
-
-
1
protected
-
# Return new blank `Context` to evaluate processors in.
-
1
def blank_context
-
environment.context_class.new(environment, logical_path.to_s, pathname)
-
end
-
-
# Get `Context` after processors have been ran on it. This
-
# trackes any dependencies that processors have added to it.
-
1
def dependency_context
-
@dependency_context ||= build_dependency_context_and_body[0]
-
end
-
-
# All paths that this asset depends on. This list may include
-
# non-assets like directories.
-
1
def dependency_paths
-
@dependency_paths ||= build_dependencies_paths_and_assets[0]
-
end
-
-
1
private
-
1
def logger
-
environment.logger
-
end
-
-
# Check if self has already been required and raise a fast
-
# error. Otherwise you end up with a StackOverflow error.
-
1
def check_circular_dependency!
-
requires = @options[:_requires] ||= []
-
if requires.include?(pathname.to_s)
-
raise CircularDependencyError, "#{pathname} has already been required"
-
end
-
requires << pathname.to_s
-
end
-
-
1
def build_dependency_context_and_body
-
start_time = Time.now.to_f
-
-
context = blank_context
-
-
# Read original data once and pass it along to `Context`
-
data = Sprockets::Utils.read_unicode(pathname)
-
-
# Prime digest cache with data, since we happen to have it
-
environment.file_digest(pathname, data)
-
-
# Runs all processors on `Context`
-
body = context.evaluate(pathname, :data => data)
-
-
@dependency_context, @body = context, body
-
-
elapsed_time = ((Time.now.to_f - start_time) * 1000).to_i
-
logger.info "Compiled #{logical_path} (#{elapsed_time}ms) (pid #{Process.pid})"
-
-
return context, body
-
end
-
-
1
def build_dependencies_paths_and_assets
-
check_circular_dependency!
-
-
paths, assets = {}, []
-
-
# Define an `add_dependency` helper
-
add_dependency = lambda do |asset|
-
unless assets.any? { |a| a.pathname == asset.pathname }
-
assets << asset
-
end
-
end
-
-
# Iterate over all the declared require paths from the `Context`
-
dependency_context._required_paths.each do |required_path|
-
# Catch `require_self`
-
if required_path == pathname.to_s
-
add_dependency.call(self)
-
else
-
# Recursively lookup required asset
-
environment[required_path, @options].to_a.each do |asset|
-
add_dependency.call(asset)
-
end
-
end
-
end
-
-
# Ensure self is added to the dependency list
-
add_dependency.call(self)
-
-
dependency_context._dependency_paths.each do |path|
-
paths[path] ||= {
-
'path' => path,
-
'mtime' => environment.stat(path).mtime,
-
'hexdigest' => environment.file_digest(path).hexdigest
-
}
-
end
-
-
dependency_context._dependency_assets.each do |path|
-
# Skip if depending on self
-
next if path == pathname.to_s
-
-
# Recursively lookup required asset
-
environment[path, @options].to_a.each do |asset|
-
asset.dependency_paths.each do |dep|
-
paths[dep['path']] ||= dep
-
end
-
end
-
end
-
-
@dependency_paths, @assets = paths.values, assets
-
-
return @dependency_paths, @assets
-
end
-
-
1
def build_source
-
hash = environment.cache_hash("#{pathname}:source", id) do
-
data = ""
-
-
# Explode Asset into parts and gather the dependency bodies
-
to_a.each { |dependency| data << dependency.body }
-
-
# Run bundle processors on concatenated source
-
data = blank_context.evaluate(pathname, :data => data,
-
:processors => environment.bundle_processors(content_type))
-
-
{ 'length' => Rack::Utils.bytesize(data),
-
'digest' => environment.digest.update(data).hexdigest,
-
'source' => data }
-
end
-
hash['length'] = Integer(hash['length']) if hash['length'].is_a?(String)
-
-
@length = hash['length']
-
@digest = hash['digest']
-
@source = hash['source']
-
-
hash
-
end
-
end
-
end
-
1
require 'sprockets/bundled_asset'
-
1
require 'sprockets/static_asset'
-
-
1
module Sprockets
-
# `Caching` is an internal mixin whose public methods are exposed on
-
# the `Environment` and `Index` classes.
-
1
module Caching
-
# Return `Asset` instance for serialized `Hash`.
-
1
def asset_from_hash(hash)
-
return unless hash.is_a?(Hash)
-
case hash['class']
-
when 'BundledAsset'
-
BundledAsset.from_hash(self, hash)
-
when 'StaticAsset'
-
StaticAsset.from_hash(self, hash)
-
else
-
nil
-
end
-
rescue Exception => e
-
logger.debug "Cache for Asset (#{hash['logical_path']}) is stale"
-
logger.debug e
-
nil
-
end
-
-
1
def cache_hash(key, version)
-
if cache.nil?
-
yield
-
elsif hash = cache_get_hash(key, version)
-
hash
-
elsif hash = yield
-
cache_set_hash(key, version, hash)
-
hash
-
end
-
end
-
-
1
protected
-
# Cache helper method. Takes a `path` argument which maybe a
-
# logical path or fully expanded path. The `&block` is passed
-
# for finding and building the asset if its not in cache.
-
1
def cache_asset(path)
-
# If `cache` is not set, return fast
-
if cache.nil?
-
yield
-
-
# Check cache for `path`
-
elsif (asset = asset_from_hash(cache_get_hash(path.to_s, digest.hexdigest))) && asset.fresh?
-
asset
-
-
# Otherwise yield block that slowly finds and builds the asset
-
elsif asset = yield
-
hash = {}
-
asset.encode_with(hash)
-
-
# Save the asset to its path
-
cache_set_hash(path.to_s, digest.hexdigest, hash)
-
-
# Since path maybe a logical or full pathname, save the
-
# asset its its full path too
-
if path.to_s != asset.pathname.to_s
-
cache_set_hash(asset.pathname.to_s, digest.hexdigest, hash)
-
end
-
-
asset
-
end
-
end
-
-
1
private
-
# Strips `Environment#root` from key to make the key work
-
# consisently across different servers. The key is also hashed
-
# so it does not exceed 250 characters.
-
1
def cache_key_for(key)
-
File.join('sprockets', digest.hexdigest(key.sub(root, '')))
-
end
-
-
1
def cache_get_hash(key, version)
-
hash = cache_get(cache_key_for(key))
-
if hash.is_a?(Hash) && version == hash['_version']
-
hash
-
end
-
end
-
-
1
def cache_set_hash(key, version, hash)
-
hash['_version'] = version
-
cache_set(cache_key_for(key), hash)
-
hash
-
end
-
-
# Low level cache getter for `key`. Checks a number of supported
-
# cache interfaces.
-
1
def cache_get(key)
-
# `Cache#get(key)` for Memcache
-
if cache.respond_to?(:get)
-
cache.get(key)
-
-
# `Cache#[key]` so `Hash` can be used
-
elsif cache.respond_to?(:[])
-
cache[key]
-
-
# `Cache#read(key)` for `ActiveSupport::Cache` support
-
elsif cache.respond_to?(:read)
-
cache.read(key)
-
-
else
-
nil
-
end
-
end
-
-
# Low level cache setter for `key`. Checks a number of supported
-
# cache interfaces.
-
1
def cache_set(key, value)
-
# `Cache#set(key, value)` for Memcache
-
if cache.respond_to?(:set)
-
cache.set(key, value)
-
-
# `Cache#[key]=value` so `Hash` can be used
-
elsif cache.respond_to?(:[]=)
-
cache[key] = value
-
-
# `Cache#write(key, value)` for `ActiveSupport::Cache` support
-
elsif cache.respond_to?(:write)
-
cache.write(key, value)
-
end
-
-
value
-
end
-
end
-
end
-
1
require 'tilt'
-
-
1
module Sprockets
-
# Some browsers have issues with stylesheets that contain multiple
-
# `@charset` definitions. The issue surfaces while using Sass since
-
# it inserts a `@charset` at the top of each file. Then Sprockets
-
# concatenates them together.
-
#
-
# The `CharsetNormalizer` processor strips out multiple `@charset`
-
# definitions.
-
#
-
# The current implementation is naive. It picks the first `@charset`
-
# it sees and strips the others. This works for most people because
-
# the other definitions are usually `UTF-8`. A more sophisticated
-
# approach would be to re-encode stylesheets with mixed encodings.
-
#
-
# This behavior can be disabled with:
-
#
-
# environment.unregister_bundle_processor 'text/css', Sprockets::CharsetNormalizer
-
#
-
1
class CharsetNormalizer < Tilt::Template
-
1
def prepare
-
end
-
-
1
def evaluate(context, locals, &block)
-
charset = nil
-
-
# Find and strip out any `@charset` definitions
-
filtered_data = data.gsub(/^@charset "([^"]+)";$/) {
-
charset ||= $1; ""
-
}
-
-
if charset
-
# If there was a charset, move it to the top
-
"@charset \"#{charset}\";#{filtered_data}"
-
else
-
data
-
end
-
end
-
end
-
end
-
1
require 'base64'
-
1
require 'rack/utils'
-
1
require 'sprockets/errors'
-
1
require 'sprockets/utils'
-
1
require 'pathname'
-
1
require 'set'
-
-
1
module Sprockets
-
# `Context` provides helper methods to all `Tilt` processors. They
-
# are typically accessed by ERB templates. You can mix in custom
-
# helpers by injecting them into `Environment#context_class`. Do not
-
# mix them into `Context` directly.
-
#
-
# environment.instance_eval do
-
# include MyHelper
-
# def asset_url; end
-
# end
-
#
-
# <%= asset_url "foo.png" %>
-
#
-
# The `Context` also collects dependencies declared by
-
# assets. See `DirectiveProcessor` for an example of this.
-
1
class Context
-
1
attr_reader :environment, :pathname
-
1
attr_reader :_required_paths, :_dependency_paths, :_dependency_assets
-
1
attr_writer :__LINE__
-
-
1
def initialize(environment, logical_path, pathname)
-
@environment = environment
-
@logical_path = logical_path
-
@pathname = pathname
-
@__LINE__ = nil
-
-
@_required_paths = []
-
@_dependency_paths = Set.new([pathname.to_s])
-
@_dependency_assets = Set.new
-
end
-
-
# Returns the environment path that contains the file.
-
#
-
# If `app/javascripts` and `app/stylesheets` are in your path, and
-
# current file is `app/javascripts/foo/bar.js`, `root_path` would
-
# return `app/javascripts`.
-
1
def root_path
-
environment.paths.detect { |path| pathname.to_s[path] }
-
end
-
-
# Returns logical path without any file extensions.
-
#
-
# 'app/javascripts/application.js'
-
# # => 'application'
-
#
-
1
def logical_path
-
@logical_path[/^([^.]+)/, 0]
-
end
-
-
# Returns content type of file
-
#
-
# 'application/javascript'
-
# 'text/css'
-
#
-
1
def content_type
-
environment.content_type_of(pathname)
-
end
-
-
# Given a logical path, `resolve` will find and return the fully
-
# expanded path. Relative paths will also be resolved. An optional
-
# `:content_type` restriction can be supplied to restrict the
-
# search.
-
#
-
# resolve("foo.js")
-
# # => "/path/to/app/javascripts/foo.js"
-
#
-
# resolve("./bar.js")
-
# # => "/path/to/app/javascripts/bar.js"
-
#
-
1
def resolve(path, options = {}, &block)
-
pathname = Pathname.new(path)
-
attributes = environment.attributes_for(pathname)
-
-
if pathname.absolute?
-
pathname
-
-
elsif content_type = options[:content_type]
-
content_type = self.content_type if content_type == :self
-
-
if attributes.format_extension
-
if content_type != attributes.content_type
-
raise ContentTypeMismatch, "#{path} is " +
-
"'#{attributes.content_type}', not '#{content_type}'"
-
end
-
end
-
-
resolve(path) do |candidate|
-
if self.content_type == environment.content_type_of(candidate)
-
return candidate
-
end
-
end
-
-
raise FileNotFound, "couldn't find file '#{path}'"
-
else
-
environment.resolve(path, :base_path => self.pathname.dirname, &block)
-
end
-
end
-
-
# `depend_on` allows you to state a dependency on a file without
-
# including it.
-
#
-
# This is used for caching purposes. Any changes made to
-
# the dependency file with invalidate the cache of the
-
# source file.
-
1
def depend_on(path)
-
@_dependency_paths << resolve(path).to_s
-
nil
-
end
-
-
# `depend_on_asset` allows you to state an asset dependency
-
# without including it.
-
#
-
# This is used for caching purposes. Any changes that would
-
# invalidate the dependency asset will invalidate the source
-
# file. Unlike `depend_on`, this will include recursively include
-
# the target asset's dependencies.
-
1
def depend_on_asset(path)
-
filename = resolve(path).to_s
-
@_dependency_assets << filename
-
nil
-
end
-
-
# `require_asset` declares `path` as a dependency of the file. The
-
# dependency will be inserted before the file and will only be
-
# included once.
-
#
-
# If ERB processing is enabled, you can use it to dynamically
-
# require assets.
-
#
-
# <%= require_asset "#{framework}.js" %>
-
#
-
1
def require_asset(path)
-
pathname = resolve(path, :content_type => :self)
-
depend_on_asset(pathname)
-
@_required_paths << pathname.to_s
-
nil
-
end
-
-
# Tests if target path is able to be safely required into the
-
# current concatenation.
-
1
def asset_requirable?(path)
-
pathname = resolve(path)
-
content_type = environment.content_type_of(pathname)
-
pathname.file? && (self.content_type.nil? || self.content_type == content_type)
-
end
-
-
# Reads `path` and runs processors on the file.
-
#
-
# This allows you to capture the result of an asset and include it
-
# directly in another.
-
#
-
# <%= evaluate "bar.js" %>
-
#
-
1
def evaluate(path, options = {})
-
pathname = resolve(path)
-
attributes = environment.attributes_for(pathname)
-
processors = options[:processors] || attributes.processors
-
-
if options[:data]
-
result = options[:data]
-
else
-
result = Sprockets::Utils.read_unicode(pathname)
-
end
-
-
processors.each do |processor|
-
begin
-
template = processor.new(pathname.to_s) { result }
-
result = template.render(self, {})
-
rescue Exception => e
-
annotate_exception! e
-
raise
-
end
-
end
-
-
result
-
end
-
-
# Returns a Base64-encoded `data:` URI with the contents of the
-
# asset at the specified path, and marks that path as a dependency
-
# of the current file.
-
#
-
# Use `asset_data_uri` from ERB with CSS or JavaScript assets:
-
#
-
# #logo { background: url(<%= asset_data_uri 'logo.png' %>) }
-
#
-
# $('<img>').attr('src', '<%= asset_data_uri 'avatar.jpg' %>')
-
#
-
1
def asset_data_uri(path)
-
depend_on(path)
-
asset = environment.find_asset(path)
-
base64 = Base64.encode64(asset.to_s).gsub(/\s+/, "")
-
"data:#{asset.content_type};base64,#{Rack::Utils.escape(base64)}"
-
end
-
-
1
private
-
# Annotates exception backtrace with the original template that
-
# the exception was raised in.
-
1
def annotate_exception!(exception)
-
location = pathname.to_s
-
location << ":#{@__LINE__}" if @__LINE__
-
-
exception.extend(Sprockets::EngineError)
-
exception.sprockets_annotation = " (in #{location})"
-
end
-
-
1
def logger
-
environment.logger
-
end
-
end
-
end
-
1
module Sprockets
-
# `Digest` is an internal mixin whose public methods are exposed on
-
# the `Environment` and `Index` classes.
-
1
module Digest
-
# Returns a `Digest` implementation class.
-
#
-
# Defaults to `Digest::MD5`.
-
1
def digest_class
-
@digest_class
-
end
-
-
# Assign a `Digest` implementation class. This maybe any Ruby
-
# `Digest::` implementation such as `Digest::MD5` or
-
# `Digest::SHA1`.
-
#
-
# environment.digest_class = Digest::SHA1
-
#
-
1
def digest_class=(klass)
-
expire_index!
-
@digest_class = klass
-
end
-
-
# The `Environment#version` is a custom value used for manually
-
# expiring all asset caches.
-
#
-
# Sprockets is able to track most file and directory changes and
-
# will take care of expiring the cache for you. However, its
-
# impossible to know when any custom helpers change that you mix
-
# into the `Context`.
-
#
-
# It would be wise to increment this value anytime you make a
-
# configuration change to the `Environment` object.
-
1
def version
-
@version
-
end
-
-
# Assign an environment version.
-
#
-
# environment.version = '2.0'
-
#
-
1
def version=(version)
-
1
expire_index!
-
1
@version = version
-
end
-
-
# Returns a `Digest` instance for the `Environment`.
-
#
-
# This value serves two purposes. If two `Environment`s have the
-
# same digest value they can be treated as equal. This is more
-
# useful for comparing environment states between processes rather
-
# than in the same. Two equal `Environment`s can share the same
-
# cached assets.
-
#
-
# The value also provides a seed digest for all `Asset`
-
# digests. Any change in the environment digest will affect all of
-
# its assets.
-
1
def digest
-
# Compute the initial digest using the implementation class. The
-
# Sprockets release version and custom environment version are
-
# mixed in. So any new releases will affect all your assets.
-
@digest ||= digest_class.new.update(VERSION).update(version.to_s)
-
-
# Returned a dupped copy so the caller can safely mutate it with `.update`
-
@digest.dup
-
end
-
end
-
end
-
1
require 'pathname'
-
1
require 'shellwords'
-
1
require 'tilt'
-
1
require 'yaml'
-
-
1
module Sprockets
-
# The `DirectiveProcessor` is responsible for parsing and evaluating
-
# directive comments in a source file.
-
#
-
# A directive comment starts with a comment prefix, followed by an "=",
-
# then the directive name, then any arguments.
-
#
-
# // JavaScript
-
# //= require "foo"
-
#
-
# # CoffeeScript
-
# #= require "bar"
-
#
-
# /* CSS
-
# *= require "baz"
-
# */
-
#
-
# The Processor is implemented as a `Tilt::Template` and is loosely
-
# coupled to Sprockets. This makes it possible to disable or modify
-
# the processor to do whatever you'd like. You could add your own
-
# custom directives or invent your own directive syntax.
-
#
-
# `Environment#processors` includes `DirectiveProcessor` by default.
-
#
-
# To remove the processor entirely:
-
#
-
# env.unregister_processor('text/css', Sprockets::DirectiveProcessor)
-
# env.unregister_processor('application/javascript', Sprockets::DirectiveProcessor)
-
#
-
# Then inject your own preprocessor:
-
#
-
# env.register_processor('text/css', MyProcessor)
-
#
-
1
class DirectiveProcessor < Tilt::Template
-
# Directives will only be picked up if they are in the header
-
# of the source file. C style (/* */), JavaScript (//), and
-
# Ruby (#) comments are supported.
-
#
-
# Directives in comments after the first non-whitespace line
-
# of code will not be processed.
-
#
-
1
HEADER_PATTERN = /
-
\A (
-
(?m:\s*) (
-
(\/\* (?m:.*?) \*\/) |
-
(\#\#\# (?m:.*?) \#\#\#) |
-
(\/\/ .* \n?)+ |
-
(\# .* \n?)+
-
)
-
)+
-
/x
-
-
# Directives are denoted by a `=` followed by the name, then
-
# argument list.
-
#
-
# A few different styles are allowed:
-
#
-
# // =require foo
-
# //= require foo
-
# //= require "foo"
-
#
-
1
DIRECTIVE_PATTERN = /
-
^ [\W]* = \s* (\w+.*?) (\*\/)? $
-
/x
-
-
1
attr_reader :pathname
-
1
attr_reader :header, :body
-
-
1
def prepare
-
@pathname = Pathname.new(file)
-
-
@header = data[HEADER_PATTERN, 0] || ""
-
@body = $' || data
-
# Ensure body ends in a new line
-
@body += "\n" if @body != "" && @body !~ /\n\Z/m
-
-
@included_pathnames = []
-
@compat = false
-
end
-
-
# Implemented for Tilt#render.
-
#
-
# `context` is a `Context` instance with methods that allow you to
-
# access the environment and append to the bundle. See `Context`
-
# for the complete API.
-
1
def evaluate(context, locals, &block)
-
@context = context
-
-
@result = ""
-
@has_written_body = false
-
-
process_directives
-
process_source
-
-
@result
-
end
-
-
# Returns the header String with any directives stripped.
-
1
def processed_header
-
lineno = 0
-
@processed_header ||= header.lines.reject { |line|
-
lineno += 1
-
directives.assoc(lineno)
-
}.join.chomp
-
end
-
-
# Returns the source String with any directives stripped.
-
1
def processed_source
-
@processed_source ||= processed_header + body
-
end
-
-
# Returns an Array of directive structures. Each structure
-
# is an Array with the line number as the first element, the
-
# directive name as the second element, followed by any
-
# arguments.
-
#
-
# [[1, "require", "foo"], [2, "require", "bar"]]
-
#
-
1
def directives
-
@directives ||= header.lines.each_with_index.map { |line, index|
-
if directive = line[DIRECTIVE_PATTERN, 1]
-
name, *args = Shellwords.shellwords(directive)
-
if respond_to?("process_#{name}_directive")
-
[index + 1, name, *args]
-
end
-
end
-
}.compact
-
end
-
-
1
protected
-
1
attr_reader :included_pathnames
-
1
attr_reader :context
-
-
# Gathers comment directives in the source and processes them.
-
# Any directive method matching `process_*_directive` will
-
# automatically be available. This makes it easy to extend the
-
# processor.
-
#
-
# To implement a custom directive called `require_glob`, subclass
-
# `Sprockets::DirectiveProcessor`, then add a method called
-
# `process_require_glob_directive`.
-
#
-
# class DirectiveProcessor < Sprockets::DirectiveProcessor
-
# def process_require_glob_directive
-
# Dir["#{pathname.dirname}/#{glob}"].sort.each do |filename|
-
# require(filename)
-
# end
-
# end
-
# end
-
#
-
# Replace the current processor on the environment with your own:
-
#
-
# env.unregister_processor('text/css', Sprockets::DirectiveProcessor)
-
# env.register_processor('text/css', DirectiveProcessor)
-
#
-
1
def process_directives
-
directives.each do |line_number, name, *args|
-
context.__LINE__ = line_number
-
send("process_#{name}_directive", *args)
-
context.__LINE__ = nil
-
end
-
end
-
-
1
def process_source
-
unless @has_written_body || processed_header.empty?
-
@result << processed_header << "\n"
-
end
-
-
included_pathnames.each do |pathname|
-
@result << context.evaluate(pathname)
-
end
-
-
unless @has_written_body
-
@result << body
-
end
-
-
if compat? && constants.any?
-
@result.gsub!(/<%=(.*?)%>/) { constants[$1.strip] }
-
end
-
end
-
-
# The `require` directive functions similar to Ruby's own `require`.
-
# It provides a way to declare a dependency on a file in your path
-
# and ensures its only loaded once before the source file.
-
#
-
# `require` works with files in the environment path:
-
#
-
# //= require "foo.js"
-
#
-
# Extensions are optional. If your source file is ".js", it
-
# assumes you are requiring another ".js".
-
#
-
# //= require "foo"
-
#
-
# Relative paths work too. Use a leading `./` to denote a relative
-
# path:
-
#
-
# //= require "./bar"
-
#
-
1
def process_require_directive(path)
-
if @compat
-
if path =~ /<([^>]+)>/
-
path = $1
-
else
-
path = "./#{path}" unless relative?(path)
-
end
-
end
-
-
context.require_asset(path)
-
end
-
-
# `require_self` causes the body of the current file to be
-
# inserted before any subsequent `require` or `include`
-
# directives. Useful in CSS files, where it's common for the
-
# index file to contain global styles that need to be defined
-
# before other dependencies are loaded.
-
#
-
# /*= require "reset"
-
# *= require_self
-
# *= require_tree .
-
# */
-
#
-
1
def process_require_self_directive
-
if @has_written_body
-
raise ArgumentError, "require_self can only be called once per source file"
-
end
-
-
context.require_asset(pathname)
-
process_source
-
included_pathnames.clear
-
@has_written_body = true
-
end
-
-
# The `include` directive works similar to `require` but
-
# inserts the contents of the dependency even if it already
-
# has been required.
-
#
-
# //= include "header"
-
#
-
1
def process_include_directive(path)
-
pathname = context.resolve(path)
-
context.depend_on_asset(pathname)
-
included_pathnames << pathname
-
end
-
-
# `require_directory` requires all the files inside a single
-
# directory. It's similar to `path/*` since it does not follow
-
# nested directories.
-
#
-
# //= require_directory "./javascripts"
-
#
-
1
def process_require_directory_directive(path = ".")
-
if relative?(path)
-
root = pathname.dirname.join(path).expand_path
-
-
unless (stats = stat(root)) && stats.directory?
-
raise ArgumentError, "require_tree argument must be a directory"
-
end
-
-
context.depend_on(root)
-
-
entries(root).each do |pathname|
-
pathname = root.join(pathname)
-
if pathname.to_s == self.file
-
next
-
elsif context.asset_requirable?(pathname)
-
context.require_asset(pathname)
-
end
-
end
-
else
-
# The path must be relative and start with a `./`.
-
raise ArgumentError, "require_directory argument must be a relative path"
-
end
-
end
-
-
# `require_tree` requires all the nested files in a directory.
-
# Its glob equivalent is `path/**/*`.
-
#
-
# //= require_tree "./public"
-
#
-
1
def process_require_tree_directive(path = ".")
-
if relative?(path)
-
root = pathname.dirname.join(path).expand_path
-
-
unless (stats = stat(root)) && stats.directory?
-
raise ArgumentError, "require_tree argument must be a directory"
-
end
-
-
context.depend_on(root)
-
-
each_entry(root) do |pathname|
-
if pathname.to_s == self.file
-
next
-
elsif stat(pathname).directory?
-
context.depend_on(pathname)
-
elsif context.asset_requirable?(pathname)
-
context.require_asset(pathname)
-
end
-
end
-
else
-
# The path must be relative and start with a `./`.
-
raise ArgumentError, "require_tree argument must be a relative path"
-
end
-
end
-
-
# Allows you to state a dependency on a file without
-
# including it.
-
#
-
# This is used for caching purposes. Any changes made to
-
# the dependency file will invalidate the cache of the
-
# source file.
-
#
-
# This is useful if you are using ERB and File.read to pull
-
# in contents from another file.
-
#
-
# //= depend_on "foo.png"
-
#
-
1
def process_depend_on_directive(path)
-
context.depend_on(path)
-
end
-
-
# Allows you to state a dependency on an asset without including
-
# it.
-
#
-
# This is used for caching purposes. Any changes that would
-
# invalid the asset dependency will invalidate the cache our the
-
# source file.
-
#
-
# Unlike `depend_on`, the path must be a requirable asset.
-
#
-
# //= depend_on_asset "bar.js"
-
#
-
1
def process_depend_on_asset_directive(path)
-
context.depend_on_asset(path)
-
end
-
-
# Enable Sprockets 1.x compat mode.
-
#
-
# Makes it possible to use the same JavaScript source
-
# file in both Sprockets 1 and 2.
-
#
-
# //= compat
-
#
-
1
def process_compat_directive
-
@compat = true
-
end
-
-
# Checks if Sprockets 1.x compat mode enabled
-
1
def compat?
-
@compat
-
end
-
-
# Sprockets 1.x allowed for constant interpolation if a
-
# constants.yml was present. This is only available if
-
# compat mode is on.
-
1
def constants
-
if compat?
-
pathname = Pathname.new(context.root_path).join("constants.yml")
-
stat(pathname) ? YAML.load_file(pathname) : {}
-
else
-
{}
-
end
-
end
-
-
# `provide` is stubbed out for Sprockets 1.x compat.
-
# Mutating the path when an asset is being built is
-
# not permitted.
-
1
def process_provide_directive(path)
-
end
-
-
1
private
-
1
def relative?(path)
-
path =~ /^\.($|\.?\/)/
-
end
-
-
1
def stat(path)
-
context.environment.stat(path)
-
end
-
-
1
def entries(path)
-
context.environment.entries(path)
-
end
-
-
1
def each_entry(root, &block)
-
context.environment.each_entry(root, &block)
-
end
-
end
-
end
-
1
require 'tilt'
-
-
1
module Sprockets
-
# Tilt engine class for the Eco compiler. Depends on the `eco` gem.
-
#
-
# For more infomation see:
-
#
-
# https://github.com/sstephenson/ruby-eco
-
# https://github.com/sstephenson/eco
-
#
-
1
class EcoTemplate < Tilt::Template
-
# Check to see if Eco is loaded
-
1
def self.engine_initialized?
-
defined? ::Eco
-
end
-
-
# Autoload eco library. If the library isn't loaded, Tilt will produce
-
# a thread safetly warning. If you intend to use `.eco` files, you
-
# should explicitly require it.
-
1
def initialize_engine
-
require_template_library 'eco'
-
end
-
-
1
def prepare
-
end
-
-
# Compile template data with Eco compiler.
-
#
-
# Returns a JS function definition String. The result should be
-
# assigned to a JS variable.
-
#
-
# # => "function(...) {...}"
-
#
-
1
def evaluate(scope, locals, &block)
-
Eco.compile(data)
-
end
-
end
-
end
-
1
require 'tilt'
-
-
1
module Sprockets
-
# Tilt engine class for the EJS compiler. Depends on the `ejs` gem.
-
#
-
# For more infomation see:
-
#
-
# https://github.com/sstephenson/ruby-ejs
-
#
-
1
class EjsTemplate < Tilt::Template
-
# Check to see if EJS is loaded
-
1
def self.engine_initialized?
-
defined? ::EJS
-
end
-
-
# Autoload ejs library. If the library isn't loaded, Tilt will produce
-
# a thread safetly warning. If you intend to use `.ejs` files, you
-
# should explicitly require it.
-
1
def initialize_engine
-
require_template_library 'ejs'
-
end
-
-
1
def prepare
-
end
-
-
# Compile template data with EJS compiler.
-
#
-
# Returns a JS function definition String. The result should be
-
# assigned to a JS variable.
-
#
-
# # => "function(obj){...}"
-
#
-
1
def evaluate(scope, locals, &block)
-
EJS.compile(data)
-
end
-
end
-
end
-
1
require 'sprockets/eco_template'
-
1
require 'sprockets/ejs_template'
-
1
require 'sprockets/jst_processor'
-
1
require 'sprockets/utils'
-
1
require 'tilt'
-
-
1
module Sprockets
-
# `Engines` provides a global and `Environment` instance registry.
-
#
-
# An engine is a type of processor that is bound to an filename
-
# extension. `application.js.coffee` indicates that the
-
# `CoffeeScriptTemplate` engine will be ran on the file.
-
#
-
# Extensions can be stacked and will be evaulated from right to
-
# left. `application.js.coffee.erb` will first run `ERBTemplate`
-
# then `CoffeeScriptTemplate`.
-
#
-
# All `Engine`s must follow the `Tilt::Template` interface. It is
-
# recommended to subclass `Tilt::Template`.
-
#
-
# Its recommended that you register engine changes on your local
-
# `Environment` instance.
-
#
-
# environment.register_engine '.foo', FooProcessor
-
#
-
# The global registry is exposed for plugins to register themselves.
-
#
-
# Sprockets.register_engine '.sass', SassTemplate
-
#
-
1
module Engines
-
# Returns an `Array` of `Engine`s registered on the
-
# `Environment`. If an `ext` argument is supplied, the `Engine`
-
# register under that extension will be returned.
-
#
-
# environment.engines
-
# # => [CoffeeScriptTemplate, SassTemplate, ...]
-
#
-
# environment.engines('.coffee')
-
# # => CoffeeScriptTemplate
-
#
-
1
def engines(ext = nil)
-
1
if ext
-
ext = Sprockets::Utils.normalize_extension(ext)
-
@engines[ext]
-
else
-
1
@engines.dup
-
end
-
end
-
-
# Returns an `Array` of engine extension `String`s.
-
#
-
# environment.engine_extensions
-
# # => ['.coffee', '.sass', ...]
-
1
def engine_extensions
-
@engines.keys
-
end
-
-
# Registers a new Engine `klass` for `ext`. If the `ext` already
-
# has an engine registered, it will be overridden.
-
#
-
# environment.register_engine '.coffee', CoffeeScriptTemplate
-
#
-
1
def register_engine(ext, klass)
-
11
ext = Sprockets::Utils.normalize_extension(ext)
-
11
@engines[ext] = klass
-
end
-
-
1
private
-
1
def deep_copy_hash(hash)
-
initial = Hash.new { |h, k| h[k] = [] }
-
hash.inject(initial) { |h, (k, a)| h[k] = a.dup; h }
-
end
-
end
-
-
# Extend Sprockets module to provide global registry
-
1
extend Engines
-
1
@engines = {}
-
-
# Cherry pick the default Tilt engines that make sense for
-
# Sprockets. We don't need ones that only generate html like HAML.
-
-
# Mmm, CoffeeScript
-
1
register_engine '.coffee', Tilt::CoffeeScriptTemplate
-
-
# JST engines
-
1
register_engine '.jst', JstProcessor
-
1
register_engine '.eco', EcoTemplate
-
1
register_engine '.ejs', EjsTemplate
-
-
# CSS engines
-
1
register_engine '.less', Tilt::LessTemplate
-
1
register_engine '.sass', Tilt::SassTemplate
-
1
register_engine '.scss', Tilt::ScssTemplate
-
-
# Other
-
1
register_engine '.erb', Tilt::ERBTemplate
-
1
register_engine '.str', Tilt::StringTemplate
-
end
-
1
require 'sprockets/base'
-
1
require 'sprockets/charset_normalizer'
-
1
require 'sprockets/context'
-
1
require 'sprockets/directive_processor'
-
1
require 'sprockets/index'
-
1
require 'sprockets/safety_colons'
-
-
1
require 'hike'
-
1
require 'logger'
-
1
require 'pathname'
-
1
require 'tilt'
-
-
1
module Sprockets
-
1
class Environment < Base
-
# `Environment` should initialized with your application's root
-
# directory. This should be the same as your Rails or Rack root.
-
#
-
# env = Environment.new(Rails.root)
-
#
-
1
def initialize(root = ".")
-
1
@trail = Hike::Trail.new(root)
-
-
1
self.logger = Logger.new($stderr)
-
1
self.logger.level = Logger::FATAL
-
-
# Create a safe `Context` subclass to mutate
-
1
@context_class = Class.new(Context)
-
-
# Set MD5 as the default digest
-
1
require 'digest/md5'
-
1
@digest_class = ::Digest::MD5
-
1
@version = ''
-
-
1
@mime_types = {}
-
1
@engines = Sprockets.engines
-
3
@preprocessors = Hash.new { |h, k| h[k] = [] }
-
2
@postprocessors = Hash.new { |h, k| h[k] = [] }
-
2
@bundle_processors = Hash.new { |h, k| h[k] = [] }
-
-
1
@engines.each do |ext, klass|
-
9
add_engine_to_trail(ext, klass)
-
end
-
-
1
register_mime_type 'text/css', '.css'
-
1
register_mime_type 'application/javascript', '.js'
-
-
1
register_preprocessor 'text/css', DirectiveProcessor
-
1
register_preprocessor 'application/javascript', DirectiveProcessor
-
-
1
register_postprocessor 'application/javascript', SafetyColons
-
1
register_bundle_processor 'text/css', CharsetNormalizer
-
-
1
expire_index!
-
-
1
yield self if block_given?
-
end
-
-
# Returns a cached version of the environment.
-
#
-
# All its file system calls are cached which makes `index` much
-
# faster. This behavior is ideal in production since the file
-
# system only changes between deploys.
-
1
def index
-
Index.new(self)
-
end
-
-
# Cache `find_asset` calls
-
1
def find_asset(path, options = {})
-
# Ensure inmemory cached assets are still fresh on every lookup
-
if (asset = @assets[path.to_s]) && asset.fresh?
-
asset
-
elsif asset = super
-
@assets[path.to_s] = @assets[asset.pathname.to_s] = asset
-
asset
-
end
-
end
-
-
1
protected
-
# Cache asset building in persisted cache.
-
1
def build_asset(path, pathname, options)
-
# Persisted cache
-
cache_asset(pathname.to_s) do
-
super
-
end
-
end
-
-
1
def expire_index!
-
# Clear digest to be recomputed
-
14
@digest = nil
-
14
@assets = {}
-
end
-
end
-
end
-
# Define some basic Sprockets error classes
-
1
module Sprockets
-
1
class Error < StandardError; end
-
1
class ArgumentError < Error; end
-
1
class CircularDependencyError < Error; end
-
1
class ContentTypeMismatch < Error; end
-
1
class EncodingError < Error; end
-
1
class FileNotFound < Error; end
-
1
class FileOutsidePaths < Error; end
-
-
1
module EngineError
-
1
attr_accessor :sprockets_annotation
-
-
1
def message
-
[super, sprockets_annotation].compact.join("\n")
-
end
-
end
-
end
-
1
require 'sprockets/base'
-
-
1
module Sprockets
-
# `Index` is a special cached version of `Environment`.
-
#
-
# The expection is that all of its file system methods are cached
-
# for the instances lifetime. This makes `Index` much faster. This
-
# behavior is ideal in production environments where the file system
-
# is immutable.
-
#
-
# `Index` should not be initialized directly. Instead use
-
# `Environment#index`.
-
1
class Index < Base
-
1
def initialize(environment)
-
# Copy environment attributes
-
@logger = environment.logger
-
@context_class = environment.context_class
-
@cache = environment.cache
-
@trail = environment.trail.index
-
@digest = environment.digest
-
@digest_class = environment.digest_class
-
@version = environment.version
-
@mime_types = environment.mime_types
-
@engines = environment.engines
-
@preprocessors = environment.preprocessors
-
@postprocessors = environment.postprocessors
-
@bundle_processors = environment.bundle_processors
-
-
# Initialize caches
-
@assets = {}
-
@digests = {}
-
end
-
-
# No-op return self as index
-
1
def index
-
self
-
end
-
-
# Cache calls to `file_digest`
-
1
def file_digest(pathname, data = nil)
-
memoize(@digests, pathname.to_s) { super }
-
end
-
-
# Cache `find_asset` calls
-
1
def find_asset(path, options = {})
-
if asset = @assets[path.to_s]
-
asset
-
elsif asset = super
-
# Cache at logical path and expanded path
-
@assets[path.to_s] = @assets[asset.pathname.to_s] = asset
-
asset
-
end
-
end
-
-
1
protected
-
# Index is immutable, any methods that try to clear the cache
-
# should bomb.
-
1
def expire_index!
-
raise TypeError, "can't modify immutable index"
-
end
-
-
# Cache asset building in memory and in persisted cache.
-
1
def build_asset(path, pathname, options)
-
# Memory cache
-
memoize(@assets, pathname.to_s) do
-
# Persisted cache
-
cache_asset(pathname.to_s) do
-
super
-
end
-
end
-
end
-
-
1
private
-
# Simple memoize helper that stores `nil` values
-
1
def memoize(hash, key)
-
hash.key?(key) ? hash[key] : hash[key] = yield
-
end
-
end
-
end
-
1
require 'tilt'
-
-
1
module Sprockets
-
1
class JstProcessor < Tilt::Template
-
1
def self.default_mime_type
-
2
'application/javascript'
-
end
-
-
1
def self.default_namespace
-
'this.JST'
-
end
-
-
1
def prepare
-
@namespace = self.class.default_namespace
-
end
-
-
1
attr_reader :namespace
-
-
1
def evaluate(scope, locals, &block)
-
<<-JST
-
(function() {
-
#{namespace} || (#{namespace} = {});
-
#{namespace}[#{scope.logical_path.inspect}] = #{indent(data)};
-
}).call(this);
-
JST
-
end
-
-
1
private
-
1
def indent(string)
-
string.gsub(/$(.)/m, "\\1 ").strip
-
end
-
end
-
end
-
1
require 'rack/mime'
-
-
1
module Sprockets
-
1
module Mime
-
# Returns a `Hash` of registered mime types registered on the
-
# environment and those part of `Rack::Mime`.
-
#
-
# If an `ext` is given, it will lookup the mime type for that extension.
-
1
def mime_types(ext = nil)
-
5
if ext.nil?
-
5
Rack::Mime::MIME_TYPES.merge(@mime_types)
-
else
-
ext = Sprockets::Utils.normalize_extension(ext)
-
@mime_types[ext] || Rack::Mime::MIME_TYPES[ext]
-
end
-
end
-
-
1
if {}.respond_to?(:key)
-
1
def extension_for_mime_type(type)
-
5
mime_types.key(type)
-
end
-
else
-
def extension_for_mime_type(type)
-
mime_types.index(type)
-
end
-
end
-
-
# Register a new mime type.
-
1
def register_mime_type(mime_type, ext)
-
2
ext = Sprockets::Utils.normalize_extension(ext)
-
2
@mime_types[ext] = mime_type
-
end
-
end
-
-
# Extend Sprockets module to provide global registry
-
1
extend Mime
-
1
@mime_types = {}
-
end
-
1
require 'sprockets/engines'
-
1
require 'sprockets/mime'
-
1
require 'sprockets/processor'
-
1
require 'sprockets/utils'
-
-
1
module Sprockets
-
# `Processing` is an internal mixin whose public methods are exposed on
-
# the `Environment` and `Index` classes.
-
1
module Processing
-
1
include Engines, Mime
-
-
# Register a new mime type.
-
1
def register_mime_type(mime_type, ext)
-
# Overrides the global behavior to expire the index
-
2
expire_index!
-
2
@trail.append_extension(ext)
-
2
super
-
end
-
-
# Returns an `Array` of format extension `String`s.
-
#
-
# format_extensions
-
# # => ['.js', '.css']
-
#
-
1
def format_extensions
-
@trail.extensions - @engines.keys
-
end
-
-
# Registers a new Engine `klass` for `ext`.
-
1
def register_engine(ext, klass)
-
# Overrides the global behavior to expire the index
-
expire_index!
-
add_engine_to_trail(ext, klass)
-
super
-
end
-
-
# Deprecated alias for `preprocessors`.
-
1
def processors(*args)
-
preprocessors(*args)
-
end
-
-
# Returns an `Array` of `Processor` classes. If a `mime_type`
-
# argument is supplied, the processors registered under that
-
# extension will be returned.
-
#
-
# Preprocessors are ran before Postprocessors and Engine
-
# processors.
-
#
-
# All `Processor`s must follow the `Tilt::Template` interface. It is
-
# recommended to subclass `Tilt::Template`.
-
1
def preprocessors(mime_type = nil)
-
if mime_type
-
@preprocessors[mime_type].dup
-
else
-
deep_copy_hash(@preprocessors)
-
end
-
end
-
-
# Returns an `Array` of `Processor` classes. If a `mime_type`
-
# argument is supplied, the processors registered under that
-
# extension will be returned.
-
#
-
# Postprocessors are ran after Preprocessors and Engine processors.
-
#
-
# All `Processor`s must follow the `Tilt::Template` interface. It is
-
# recommended to subclass `Tilt::Template`.
-
1
def postprocessors(mime_type = nil)
-
if mime_type
-
@postprocessors[mime_type].dup
-
else
-
deep_copy_hash(@postprocessors)
-
end
-
end
-
-
# Deprecated alias for `register_preprocessor`.
-
1
def register_processor(*args, &block)
-
register_preprocessor(*args, &block)
-
end
-
-
# Registers a new Preprocessor `klass` for `mime_type`.
-
#
-
# register_preprocessor 'text/css', Sprockets::DirectiveProcessor
-
#
-
# A block can be passed for to create a shorthand processor.
-
#
-
# register_preprocessor :my_processor do |context, data|
-
# data.gsub(...)
-
# end
-
#
-
1
def register_preprocessor(mime_type, klass, &block)
-
2
expire_index!
-
-
2
if block_given?
-
name = klass.to_s
-
klass = Class.new(Processor) do
-
@name = name
-
@processor = block
-
end
-
end
-
-
2
@preprocessors[mime_type].push(klass)
-
end
-
-
# Registers a new Postprocessor `klass` for `mime_type`.
-
#
-
# register_postprocessor 'text/css', Sprockets::CharsetNormalizer
-
#
-
# A block can be passed for to create a shorthand processor.
-
#
-
# register_postprocessor :my_processor do |context, data|
-
# data.gsub(...)
-
# end
-
#
-
1
def register_postprocessor(mime_type, klass, &block)
-
1
expire_index!
-
-
1
if block_given?
-
name = klass.to_s
-
klass = Class.new(Processor) do
-
@name = name
-
@processor = block
-
end
-
end
-
-
1
@postprocessors[mime_type].push(klass)
-
end
-
-
# Deprecated alias for `unregister_preprocessor`.
-
1
def unregister_processor(*args)
-
unregister_preprocessor(*args)
-
end
-
-
# Remove Preprocessor `klass` for `mime_type`.
-
#
-
# unregister_preprocessor 'text/css', Sprockets::DirectiveProcessor
-
#
-
1
def unregister_preprocessor(mime_type, klass)
-
expire_index!
-
-
if klass.is_a?(String) || klass.is_a?(Symbol)
-
klass = @preprocessors[mime_type].detect { |cls|
-
cls.respond_to?(:name) &&
-
cls.name == "Sprockets::Processor (#{klass})"
-
}
-
end
-
-
@preprocessors[mime_type].delete(klass)
-
end
-
-
# Remove Postprocessor `klass` for `mime_type`.
-
#
-
# unregister_postprocessor 'text/css', Sprockets::DirectiveProcessor
-
#
-
1
def unregister_postprocessor(mime_type, klass)
-
expire_index!
-
-
if klass.is_a?(String) || klass.is_a?(Symbol)
-
klass = @postprocessors[mime_type].detect { |cls|
-
cls.respond_to?(:name) &&
-
cls.name == "Sprockets::Processor (#{klass})"
-
}
-
end
-
-
@postprocessors[mime_type].delete(klass)
-
end
-
-
# Returns an `Array` of `Processor` classes. If a `mime_type`
-
# argument is supplied, the processors registered under that
-
# extension will be returned.
-
#
-
# Bundle Processors are ran on concatenated assets rather than
-
# individual files.
-
#
-
# All `Processor`s must follow the `Tilt::Template` interface. It is
-
# recommended to subclass `Tilt::Template`.
-
1
def bundle_processors(mime_type = nil)
-
if mime_type
-
@bundle_processors[mime_type].dup
-
else
-
deep_copy_hash(@bundle_processors)
-
end
-
end
-
-
# Registers a new Bundle Processor `klass` for `mime_type`.
-
#
-
# register_bundle_processor 'text/css', Sprockets::CharsetNormalizer
-
#
-
# A block can be passed for to create a shorthand processor.
-
#
-
# register_bundle_processor :my_processor do |context, data|
-
# data.gsub(...)
-
# end
-
#
-
1
def register_bundle_processor(mime_type, klass, &block)
-
1
expire_index!
-
-
1
if block_given?
-
name = klass.to_s
-
klass = Class.new(Processor) do
-
@name = name
-
@processor = block
-
end
-
end
-
-
1
@bundle_processors[mime_type].push(klass)
-
end
-
-
# Remove Bundle Processor `klass` for `mime_type`.
-
#
-
# unregister_bundle_processor 'text/css', Sprockets::CharsetNormalizer
-
#
-
1
def unregister_bundle_processor(mime_type, klass)
-
expire_index!
-
-
if klass.is_a?(String) || klass.is_a?(Symbol)
-
klass = @bundle_processors[mime_type].detect { |cls|
-
cls.respond_to?(:name) &&
-
cls.name == "Sprockets::Processor (#{klass})"
-
}
-
end
-
-
@bundle_processors[mime_type].delete(klass)
-
end
-
-
# Return CSS compressor or nil if none is set
-
1
def css_compressor
-
bundle_processors('text/css').detect { |klass|
-
klass.respond_to?(:name) &&
-
klass.name == 'Sprockets::Processor (css_compressor)'
-
}
-
end
-
-
# Assign a compressor to run on `text/css` assets.
-
#
-
# The compressor object must respond to `compress` or `compile`.
-
1
def css_compressor=(compressor)
-
expire_index!
-
-
unregister_bundle_processor 'text/css', :css_compressor
-
return unless compressor
-
-
register_bundle_processor 'text/css', :css_compressor do |context, data|
-
compressor.compress(data)
-
end
-
end
-
-
# Return JS compressor or nil if none is set
-
1
def js_compressor
-
bundle_processors('application/javascript').detect { |klass|
-
klass.respond_to?(:name) &&
-
klass.name == 'Sprockets::Processor (js_compressor)'
-
}
-
end
-
-
# Assign a compressor to run on `application/javascript` assets.
-
#
-
# The compressor object must respond to `compress` or `compile`.
-
1
def js_compressor=(compressor)
-
expire_index!
-
-
unregister_bundle_processor 'application/javascript', :js_compressor
-
return unless compressor
-
-
register_bundle_processor 'application/javascript', :js_compressor do |context, data|
-
compressor.compress(data)
-
end
-
end
-
-
1
private
-
1
def add_engine_to_trail(ext, klass)
-
9
@trail.append_extension(ext.to_s)
-
-
9
if klass.respond_to?(:default_mime_type) && klass.default_mime_type
-
5
if format_ext = extension_for_mime_type(klass.default_mime_type)
-
5
@trail.alias_extension(ext.to_s, format_ext)
-
end
-
end
-
end
-
end
-
end
-
1
require 'tilt'
-
-
1
module Sprockets
-
# `Processor` creates an anonymous processor class from a block.
-
#
-
# register_preprocessor :my_processor do |context, data|
-
# # ...
-
# end
-
#
-
1
class Processor < Tilt::Template
-
# `processor` is a lambda or block
-
1
def self.processor
-
@processor
-
end
-
-
1
def self.name
-
"Sprockets::Processor (#{@name})"
-
end
-
-
1
def self.to_s
-
name
-
end
-
-
1
def prepare
-
end
-
-
# Call processor block with `context` and `data`.
-
1
def evaluate(context, locals)
-
self.class.processor.call(context, data)
-
end
-
end
-
end
-
1
require 'tilt'
-
-
1
module Sprockets
-
# For JS developers who are colonfobic, concatenating JS files using
-
# the module pattern usually leads to syntax errors.
-
#
-
# The `SafetyColons` processor will insert missing semicolons to the
-
# end of the file.
-
#
-
# This behavior can be disabled with:
-
#
-
# environment.unregister_postprocessor 'application/javascript', Sprockets::SafetyColons
-
#
-
1
class SafetyColons < Tilt::Template
-
1
def prepare
-
end
-
-
1
def evaluate(context, locals, &block)
-
# If the file is blank or ends in a semicolon, leave it as is
-
if data =~ /\A\s*\Z/m || data =~ /;\s*\Z/m
-
data
-
else
-
# Otherwise, append a semicolon and newline
-
"#{data};\n"
-
end
-
end
-
end
-
end
-
1
require 'time'
-
1
require 'uri'
-
-
1
module Sprockets
-
# `Server` is a concern mixed into `Environment` and
-
# `Index` that provides a Rack compatible `call`
-
# interface and url generation helpers.
-
1
module Server
-
# `call` implements the Rack 1.x specification which accepts an
-
# `env` Hash and returns a three item tuple with the status code,
-
# headers, and body.
-
#
-
# Mapping your environment at a url prefix will serve all assets
-
# in the path.
-
#
-
# map "/assets" do
-
# run Sprockets::Environment.new
-
# end
-
#
-
# A request for `"/assets/foo/bar.js"` will search your
-
# environment for `"foo/bar.js"`.
-
1
def call(env)
-
start_time = Time.now.to_f
-
time_elapsed = lambda { ((Time.now.to_f - start_time) * 1000).to_i }
-
-
msg = "Served asset #{env['PATH_INFO']} -"
-
-
# URLs containing a `".."` are rejected for security reasons.
-
if forbidden_request?(env)
-
return forbidden_response
-
end
-
-
# Mark session as "skipped" so no `Set-Cookie` header is set
-
env['rack.session.options'] ||= {}
-
env['rack.session.options'][:defer] = true
-
env['rack.session.options'][:skip] = true
-
-
# Extract the path from everything after the leading slash
-
path = unescape(env['PATH_INFO'].to_s.sub(/^\//, ''))
-
-
# Look up the asset.
-
asset = find_asset(path)
-
asset.to_a if asset
-
-
# `find_asset` returns nil if the asset doesn't exist
-
if asset.nil?
-
logger.info "#{msg} 404 Not Found (#{time_elapsed.call}ms)"
-
-
# Return a 404 Not Found
-
not_found_response
-
-
# Check request headers `HTTP_IF_MODIFIED_SINCE` and
-
# `HTTP_IF_NONE_MATCH` against the assets mtime and digest
-
elsif not_modified?(asset, env) || etag_match?(asset, env)
-
logger.info "#{msg} 304 Not Modified (#{time_elapsed.call}ms)"
-
-
# Return a 304 Not Modified
-
not_modified_response(asset, env)
-
-
else
-
logger.info "#{msg} 200 OK (#{time_elapsed.call}ms)"
-
-
# Return a 200 with the asset contents
-
ok_response(asset, env)
-
end
-
rescue Exception => e
-
logger.error "Error compiling asset #{path}:"
-
logger.error "#{e.class.name}: #{e.message}"
-
-
case content_type_of(path)
-
when "application/javascript"
-
# Re-throw JavaScript asset exceptions to the browser
-
logger.info "#{msg} 500 Internal Server Error\n\n"
-
return javascript_exception_response(e)
-
when "text/css"
-
# Display CSS asset exceptions in the browser
-
logger.info "#{msg} 500 Internal Server Error\n\n"
-
return css_exception_response(e)
-
else
-
raise
-
end
-
end
-
-
1
private
-
1
def forbidden_request?(env)
-
# Prevent access to files elsewhere on the file system
-
#
-
# http://example.org/assets/../../../etc/passwd
-
#
-
env["PATH_INFO"].include?("..")
-
end
-
-
# Returns a 403 Forbidden response tuple
-
1
def forbidden_response
-
[ 403, { "Content-Type" => "text/plain", "Content-Length" => "9" }, [ "Forbidden" ] ]
-
end
-
-
# Returns a 404 Not Found response tuple
-
1
def not_found_response
-
[ 404, { "Content-Type" => "text/plain", "Content-Length" => "9", "X-Cascade" => "pass" }, [ "Not found" ] ]
-
end
-
-
# Returns a JavaScript response that re-throws a Ruby exception
-
# in the browser
-
1
def javascript_exception_response(exception)
-
err = "#{exception.class.name}: #{exception.message}"
-
body = "throw Error(#{err.inspect})"
-
[ 200, { "Content-Type" => "application/javascript", "Content-Length" => Rack::Utils.bytesize(body).to_s }, [ body ] ]
-
end
-
-
# Returns a CSS response that hides all elements on the page and
-
# displays the exception
-
1
def css_exception_response(exception)
-
message = "\n#{exception.class.name}: #{exception.message}"
-
backtrace = "\n #{exception.backtrace.first}"
-
-
body = <<-CSS
-
html {
-
padding: 18px 36px;
-
}
-
-
head {
-
display: block;
-
}
-
-
body {
-
margin: 0;
-
padding: 0;
-
}
-
-
body > * {
-
display: none !important;
-
}
-
-
head:after, body:before, body:after {
-
display: block !important;
-
}
-
-
head:after {
-
font-family: sans-serif;
-
font-size: large;
-
font-weight: bold;
-
content: "Error compiling CSS asset";
-
}
-
-
body:before, body:after {
-
font-family: monospace;
-
white-space: pre-wrap;
-
}
-
-
body:before {
-
font-weight: bold;
-
content: "#{escape_css_content(message)}";
-
}
-
-
body:after {
-
content: "#{escape_css_content(backtrace)}";
-
}
-
CSS
-
-
[ 200, { "Content-Type" => "text/css;charset=utf-8", "Content-Length" => Rack::Utils.bytesize(body).to_s }, [ body ] ]
-
end
-
-
# Escape special characters for use inside a CSS content("...") string
-
1
def escape_css_content(content)
-
content.
-
gsub('\\', '\\\\005c ').
-
gsub("\n", '\\\\000a ').
-
gsub('"', '\\\\0022 ').
-
gsub('/', '\\\\002f ')
-
end
-
-
# Compare the requests `HTTP_IF_MODIFIED_SINCE` against the
-
# assets mtime
-
1
def not_modified?(asset, env)
-
env["HTTP_IF_MODIFIED_SINCE"] == asset.mtime.httpdate
-
end
-
-
# Compare the requests `HTTP_IF_NONE_MATCH` against the assets digest
-
1
def etag_match?(asset, env)
-
env["HTTP_IF_NONE_MATCH"] == etag(asset)
-
end
-
-
# Test if `?body=1` or `body=true` query param is set
-
1
def body_only?(env)
-
env["QUERY_STRING"].to_s =~ /body=(1|t)/
-
end
-
-
# Returns a 304 Not Modified response tuple
-
1
def not_modified_response(asset, env)
-
[ 304, {}, [] ]
-
end
-
-
# Returns a 200 OK response tuple
-
1
def ok_response(asset, env)
-
if body_only?(env)
-
[ 200, headers(env, asset, Rack::Utils.bytesize(asset.body)), [asset.body] ]
-
else
-
[ 200, headers(env, asset, asset.length), asset ]
-
end
-
end
-
-
1
def headers(env, asset, length)
-
Hash.new.tap do |headers|
-
# Set content type and length headers
-
headers["Content-Type"] = asset.content_type
-
headers["Content-Length"] = length.to_s
-
-
# Set caching headers
-
headers["Cache-Control"] = "public"
-
headers["Last-Modified"] = asset.mtime.httpdate
-
headers["ETag"] = etag(asset)
-
-
# If the request url contains a fingerprint, set a long
-
# expires on the response
-
if attributes_for(env["PATH_INFO"]).path_fingerprint
-
headers["Cache-Control"] << ", max-age=31536000"
-
-
# Otherwise set `must-revalidate` since the asset could be modified.
-
else
-
headers["Cache-Control"] << ", must-revalidate"
-
end
-
end
-
end
-
-
# URI.unescape is deprecated on 1.9. We need to use URI::Parser
-
# if its available.
-
1
if defined? URI::DEFAULT_PARSER
-
1
def unescape(str)
-
str = URI::DEFAULT_PARSER.unescape(str)
-
str.force_encoding(Encoding.default_internal) if Encoding.default_internal
-
str
-
end
-
else
-
def unescape(str)
-
URI.unescape(str)
-
end
-
end
-
-
# Helper to quote the assets digest for use as an ETag.
-
1
def etag(asset)
-
%("#{asset.digest}")
-
end
-
end
-
end
-
1
require 'sprockets/asset'
-
1
require 'fileutils'
-
1
require 'zlib'
-
-
1
module Sprockets
-
# `StaticAsset`s are used for files that are served verbatim without
-
# any processing or concatenation. These are typical images and
-
# other binary files.
-
1
class StaticAsset < Asset
-
# Define extra attributes to be serialized.
-
1
def self.serialized_attributes
-
super + %w( content_type mtime length digest )
-
end
-
-
1
def initialize(environment, logical_path, pathname, digest = nil)
-
super(environment, logical_path, pathname)
-
@digest = digest
-
load!
-
end
-
-
# Returns file contents as its `body`.
-
1
def body
-
# File is read everytime to avoid memory bloat of large binary files
-
pathname.open('rb') { |f| f.read }
-
end
-
-
# Checks if Asset is fresh by comparing the actual mtime and
-
# digest to the inmemory model.
-
1
def fresh?
-
# Check current mtime and digest
-
dependency_fresh?('path' => pathname, 'mtime' => mtime, 'hexdigest' => digest)
-
end
-
-
# Implemented for Rack SendFile support.
-
1
def to_path
-
pathname.to_s
-
end
-
-
# `to_s` is aliased to body since static assets can't have any dependencies.
-
1
def to_s
-
body
-
end
-
-
# Save asset to disk.
-
1
def write_to(filename, options = {})
-
# Gzip contents if filename has '.gz'
-
options[:compress] ||= File.extname(filename) == '.gz'
-
-
if options[:compress]
-
# Open file and run it through `Zlib`
-
pathname.open('rb') do |rd|
-
File.open("#{filename}+", 'wb') do |wr|
-
gz = Zlib::GzipWriter.new(wr, Zlib::BEST_COMPRESSION)
-
buf = ""
-
while rd.read(16384, buf)
-
gz.write(buf)
-
end
-
gz.close
-
end
-
end
-
else
-
# If no compression needs to be done, we can just copy it into place.
-
FileUtils.cp(pathname, "#{filename}+")
-
end
-
-
# Atomic write
-
FileUtils.mv("#{filename}+", filename)
-
-
# Set mtime correctly
-
File.utime(mtime, mtime, filename)
-
-
nil
-
ensure
-
# Ensure tmp file gets cleaned up
-
FileUtils.rm("#{filename}+") if File.exist?("#{filename}+")
-
end
-
-
1
private
-
1
def load!
-
content_type
-
mtime
-
length
-
digest
-
end
-
end
-
end
-
1
require 'sprockets/errors'
-
1
require 'pathname'
-
-
1
module Sprockets
-
# `Trail` is an internal mixin whose public methods are exposed on
-
# the `Environment` and `Index` classes.
-
1
module Trail
-
# Returns `Environment` root.
-
#
-
# All relative paths are expanded with root as its base. To be
-
# useful set this to your applications root directory. (`Rails.root`)
-
1
def root
-
trail.root.dup
-
end
-
-
# Returns an `Array` of path `String`s.
-
#
-
# These paths will be used for asset logical path lookups.
-
#
-
# Note that a copy of the `Array` is returned so mutating will
-
# have no affect on the environment. See `append_path`,
-
# `prepend_path`, and `clear_paths`.
-
1
def paths
-
trail.paths.dup
-
end
-
-
# Prepend a `path` to the `paths` list.
-
#
-
# Paths at the end of the `Array` have the least priority.
-
1
def prepend_path(path)
-
expire_index!
-
@trail.prepend_path(path)
-
end
-
-
# Append a `path` to the `paths` list.
-
#
-
# Paths at the beginning of the `Array` have a higher priority.
-
1
def append_path(path)
-
5
expire_index!
-
5
@trail.append_path(path)
-
end
-
-
# Clear all paths and start fresh.
-
#
-
# There is no mechanism for reordering paths, so its best to
-
# completely wipe the paths list and reappend them in the order
-
# you want.
-
1
def clear_paths
-
expire_index!
-
@trail.paths.dup.each { |path| @trail.remove_path(path) }
-
end
-
-
# Returns an `Array` of extensions.
-
#
-
# These extensions maybe omitted from logical path searches.
-
#
-
# # => [".js", ".css", ".coffee", ".sass", ...]
-
#
-
1
def extensions
-
trail.extensions.dup
-
end
-
-
# Finds the expanded real path for a given logical path by
-
# searching the environment's paths.
-
#
-
# resolve("application.js")
-
# # => "/path/to/app/javascripts/application.js.coffee"
-
#
-
# A `FileNotFound` exception is raised if the file does not exist.
-
1
def resolve(logical_path, options = {})
-
# If a block is given, preform an iterable search
-
if block_given?
-
args = attributes_for(logical_path).search_paths + [options]
-
trail.find(*args) do |path|
-
yield Pathname.new(path)
-
end
-
else
-
resolve(logical_path, options) do |pathname|
-
return pathname
-
end
-
raise FileNotFound, "couldn't find file '#{logical_path}'"
-
end
-
end
-
-
1
protected
-
1
def trail
-
@trail
-
end
-
-
1
def find_asset_in_path(logical_path, options = {})
-
# Strip fingerprint on logical path if there is one.
-
# Not sure how valuable this feature is...
-
if fingerprint = attributes_for(logical_path).path_fingerprint
-
pathname = resolve(logical_path.to_s.sub("-#{fingerprint}", ''))
-
else
-
pathname = resolve(logical_path)
-
end
-
rescue FileNotFound
-
nil
-
else
-
# Build the asset for the actual pathname
-
asset = build_asset(logical_path, pathname, options)
-
-
# Double check request fingerprint against actual digest
-
# Again, not sure if this code path is even reachable
-
if fingerprint && fingerprint != asset.digest
-
logger.error "Nonexistent asset #{logical_path} @ #{fingerprint}"
-
asset = nil
-
end
-
-
asset
-
end
-
end
-
end
-
1
module Sprockets
-
# `Utils`, we didn't know where else to put it!
-
1
module Utils
-
# If theres encoding support (aka Ruby 1.9)
-
1
if "".respond_to?(:valid_encoding?)
-
# Define UTF-8 BOM pattern matcher.
-
# Avoid using a Regexp literal because it inheirts the files
-
# encoding and we want to avoid syntax errors in other interpreters.
-
1
UTF8_BOM_PATTERN = Regexp.new("\\A\uFEFF".encode('utf-8'))
-
-
1
def self.read_unicode(pathname)
-
pathname.read.tap do |data|
-
# Eager validate the file's encoding. In most cases we
-
# expect it to be UTF-8 unless `default_external` is set to
-
# something else. An error is usually raised if the file is
-
# saved as UTF-16 when we expected UTF-8.
-
if !data.valid_encoding?
-
raise EncodingError, "#{pathname} has a invalid " +
-
"#{data.encoding} byte sequence"
-
-
# If the file is UTF-8 and theres a BOM, strip it for safe concatenation.
-
elsif data.encoding.name == "UTF-8" && data =~ UTF8_BOM_PATTERN
-
data.sub!(UTF8_BOM_PATTERN, "")
-
end
-
end
-
end
-
-
else
-
# Define UTF-8 and UTF-16 BOM pattern matchers.
-
# Avoid using a Regexp literal to prevent syntax errors in other interpreters.
-
UTF8_BOM_PATTERN = Regexp.new("\\A\\xEF\\xBB\\xBF")
-
UTF16_BOM_PATTERN = Regexp.new("\\A(\\xFE\\xFF|\\xFF\\xFE)")
-
-
def self.read_unicode(pathname)
-
pathname.read.tap do |data|
-
# If the file is UTF-8 and theres a BOM, strip it for safe concatenation.
-
if data =~ UTF8_BOM_PATTERN
-
data.sub!(UTF8_BOM_PATTERN, "")
-
-
# If we find a UTF-16 BOM, theres nothing we can do on
-
# 1.8. Only UTF-8 is supported.
-
elsif data =~ UTF16_BOM_PATTERN
-
raise EncodingError, "#{pathname} has a UTF-16 BOM. " +
-
"Resave the file as UTF-8 or upgrade to Ruby 1.9."
-
end
-
end
-
end
-
end
-
-
# Prepends a leading "." to an extension if its missing.
-
#
-
# normalize_extension("js")
-
# # => ".js"
-
#
-
# normalize_extension(".css")
-
# # => ".css"
-
#
-
1
def self.normalize_extension(extension)
-
13
extension = extension.to_s
-
13
if extension[/^\./]
-
13
extension
-
else
-
".#{extension}"
-
end
-
end
-
end
-
end
-
1
module Sprockets
-
1
VERSION = "2.0.3"
-
end
-
# support multiple ruby version (fat binaries under windows)
-
1
begin
-
1
RUBY_VERSION =~ /(\d+\.\d+)/
-
1
require "sqlite3/#{$1}/sqlite3_native"
-
rescue LoadError
-
1
require 'sqlite3/sqlite3_native'
-
end
-
-
1
require 'sqlite3/database'
-
1
require 'sqlite3/version'
-
2
module SQLite3 ; module Constants
-
-
1
module TextRep
-
1
UTF8 = 1
-
1
UTF16LE = 2
-
1
UTF16BE = 3
-
1
UTF16 = 4
-
1
ANY = 5
-
end
-
-
1
module ColumnType
-
1
INTEGER = 1
-
1
FLOAT = 2
-
1
TEXT = 3
-
1
BLOB = 4
-
1
NULL = 5
-
end
-
-
1
module ErrorCode
-
1
OK = 0 # Successful result
-
1
ERROR = 1 # SQL error or missing database
-
1
INTERNAL = 2 # An internal logic error in SQLite
-
1
PERM = 3 # Access permission denied
-
1
ABORT = 4 # Callback routine requested an abort
-
1
BUSY = 5 # The database file is locked
-
1
LOCKED = 6 # A table in the database is locked
-
1
NOMEM = 7 # A malloc() failed
-
1
READONLY = 8 # Attempt to write a readonly database
-
1
INTERRUPT = 9 # Operation terminated by sqlite_interrupt()
-
1
IOERR = 10 # Some kind of disk I/O error occurred
-
1
CORRUPT = 11 # The database disk image is malformed
-
1
NOTFOUND = 12 # (Internal Only) Table or record not found
-
1
FULL = 13 # Insertion failed because database is full
-
1
CANTOPEN = 14 # Unable to open the database file
-
1
PROTOCOL = 15 # Database lock protocol error
-
1
EMPTY = 16 # (Internal Only) Database table is empty
-
1
SCHEMA = 17 # The database schema changed
-
1
TOOBIG = 18 # Too much data for one row of a table
-
1
CONSTRAINT = 19 # Abort due to contraint violation
-
1
MISMATCH = 20 # Data type mismatch
-
1
MISUSE = 21 # Library used incorrectly
-
1
NOLFS = 22 # Uses OS features not supported on host
-
1
AUTH = 23 # Authorization denied
-
-
1
ROW = 100 # sqlite_step() has another row ready
-
1
DONE = 101 # sqlite_step() has finished executing
-
end
-
-
end ; end
-
1
require 'sqlite3/constants'
-
1
require 'sqlite3/errors'
-
1
require 'sqlite3/pragmas'
-
1
require 'sqlite3/statement'
-
1
require 'sqlite3/translator'
-
1
require 'sqlite3/value'
-
-
1
module SQLite3
-
-
# The Database class encapsulates a single connection to a SQLite3 database.
-
# Its usage is very straightforward:
-
#
-
# require 'sqlite3'
-
#
-
# SQLite3::Database.new( "data.db" ) do |db|
-
# db.execute( "select * from table" ) do |row|
-
# p row
-
# end
-
# end
-
#
-
# It wraps the lower-level methods provides by the selected driver, and
-
# includes the Pragmas module for access to various pragma convenience
-
# methods.
-
#
-
# The Database class provides type translation services as well, by which
-
# the SQLite3 data types (which are all represented as strings) may be
-
# converted into their corresponding types (as defined in the schemas
-
# for their tables). This translation only occurs when querying data from
-
# the database--insertions and updates are all still typeless.
-
#
-
# Furthermore, the Database class has been designed to work well with the
-
# ArrayFields module from Ara Howard. If you require the ArrayFields
-
# module before performing a query, and if you have not enabled results as
-
# hashes, then the results will all be indexible by field name.
-
1
class Database
-
1
attr_reader :collations
-
-
1
include Pragmas
-
-
1
class << self
-
-
1
alias :open :new
-
-
# Quotes the given string, making it safe to use in an SQL statement.
-
# It replaces all instances of the single-quote character with two
-
# single-quote characters. The modified string is returned.
-
1
def quote( string )
-
string.gsub( /'/, "''" )
-
end
-
-
end
-
-
# A boolean that indicates whether rows in result sets should be returned
-
# as hashes or not. By default, rows are returned as arrays.
-
1
attr_accessor :results_as_hash
-
-
1
def type_translation= value # :nodoc:
-
warn(<<-eowarn) if $VERBOSE
-
#{caller[0]} is calling SQLite3::Database#type_translation=
-
SQLite3::Database#type_translation= is deprecated and will be removed
-
in version 2.0.0.
-
eowarn
-
@type_translation = value
-
end
-
1
attr_reader :type_translation # :nodoc:
-
-
# Return the type translator employed by this database instance. Each
-
# database instance has its own type translator; this allows for different
-
# type handlers to be installed in each instance without affecting other
-
# instances. Furthermore, the translators are instantiated lazily, so that
-
# if a database does not use type translation, it will not be burdened by
-
# the overhead of a useless type translator. (See the Translator class.)
-
1
def translator
-
@translator ||= Translator.new
-
end
-
-
# Installs (or removes) a block that will be invoked for every access
-
# to the database. If the block returns 0 (or +nil+), the statement
-
# is allowed to proceed. Returning 1 causes an authorization error to
-
# occur, and returning 2 causes the access to be silently denied.
-
1
def authorizer( &block )
-
self.authorizer = block
-
end
-
-
# Returns a Statement object representing the given SQL. This does not
-
# execute the statement; it merely prepares the statement for execution.
-
#
-
# The Statement can then be executed using Statement#execute.
-
#
-
1
def prepare sql
-
9
stmt = SQLite3::Statement.new( self, sql )
-
9
return stmt unless block_given?
-
-
begin
-
yield stmt
-
ensure
-
stmt.close
-
end
-
end
-
-
# Executes the given SQL statement. If additional parameters are given,
-
# they are treated as bind variables, and are bound to the placeholders in
-
# the query.
-
#
-
# Note that if any of the values passed to this are hashes, then the
-
# key/value pairs are each bound separately, with the key being used as
-
# the name of the placeholder to bind the value to.
-
#
-
# The block is optional. If given, it will be invoked for each row returned
-
# by the query. Otherwise, any results are accumulated into an array and
-
# returned wholesale.
-
#
-
# See also #execute2, #query, and #execute_batch for additional ways of
-
# executing statements.
-
1
def execute sql, bind_vars = [], *args, &block
-
# FIXME: This is a terrible hack and should be removed but is required
-
# for older versions of rails
-
hack = Object.const_defined?(:ActiveRecord) && sql =~ /^PRAGMA index_list/
-
-
if bind_vars.nil? || !args.empty?
-
if args.empty?
-
bind_vars = []
-
else
-
bind_vars = [bind_vars] + args
-
end
-
-
warn(<<-eowarn) if $VERBOSE
-
#{caller[0]} is calling SQLite3::Database#execute with nil or multiple bind params
-
without using an array. Please switch to passing bind parameters as an array.
-
Support for bind parameters as *args will be removed in 2.0.0.
-
eowarn
-
end
-
-
prepare( sql ) do |stmt|
-
stmt.bind_params(bind_vars)
-
columns = stmt.columns
-
stmt = ResultSet.new(self, stmt).to_a if type_translation
-
-
if block_given?
-
stmt.each do |row|
-
if @results_as_hash
-
yield type_translation ? row : ordered_map_for(columns, row)
-
else
-
yield row
-
end
-
end
-
else
-
if @results_as_hash
-
stmt.map { |row|
-
h = type_translation ? row : ordered_map_for(columns, row)
-
-
# FIXME UGH TERRIBLE HACK!
-
h['unique'] = h['unique'].to_s if hack
-
-
h
-
}
-
else
-
stmt.to_a
-
end
-
end
-
end
-
end
-
-
# Executes the given SQL statement, exactly as with #execute. However, the
-
# first row returned (either via the block, or in the returned array) is
-
# always the names of the columns. Subsequent rows correspond to the data
-
# from the result set.
-
#
-
# Thus, even if the query itself returns no rows, this method will always
-
# return at least one row--the names of the columns.
-
#
-
# See also #execute, #query, and #execute_batch for additional ways of
-
# executing statements.
-
1
def execute2( sql, *bind_vars )
-
prepare( sql ) do |stmt|
-
result = stmt.execute( *bind_vars )
-
if block_given?
-
yield stmt.columns
-
result.each { |row| yield row }
-
else
-
return result.inject( [ stmt.columns ] ) { |arr,row|
-
arr << row; arr }
-
end
-
end
-
end
-
-
# Executes all SQL statements in the given string. By contrast, the other
-
# means of executing queries will only execute the first statement in the
-
# string, ignoring all subsequent statements. This will execute each one
-
# in turn. The same bind parameters, if given, will be applied to each
-
# statement.
-
#
-
# This always returns +nil+, making it unsuitable for queries that return
-
# rows.
-
1
def execute_batch( sql, bind_vars = [], *args )
-
# FIXME: remove this stuff later
-
unless [Array, Hash].include?(bind_vars.class)
-
bind_vars = [bind_vars]
-
warn(<<-eowarn) if $VERBOSE
-
#{caller[0]} is calling SQLite3::Database#execute_batch with bind parameters
-
that are not a list of a hash. Please switch to passing bind parameters as an
-
array or hash. Support for this behavior will be removed in version 2.0.0.
-
eowarn
-
end
-
-
# FIXME: remove this stuff later
-
if bind_vars.nil? || !args.empty?
-
if args.empty?
-
bind_vars = []
-
else
-
bind_vars = [nil] + args
-
end
-
-
warn(<<-eowarn) if $VERBOSE
-
#{caller[0]} is calling SQLite3::Database#execute_batch with nil or multiple bind params
-
without using an array. Please switch to passing bind parameters as an array.
-
Support for this behavior will be removed in version 2.0.0.
-
eowarn
-
end
-
-
sql = sql.strip
-
until sql.empty? do
-
prepare( sql ) do |stmt|
-
# FIXME: this should probably use sqlite3's api for batch execution
-
# This implementation requires stepping over the results.
-
if bind_vars.length == stmt.bind_parameter_count
-
stmt.bind_params(bind_vars)
-
end
-
stmt.step
-
sql = stmt.remainder.strip
-
end
-
end
-
nil
-
end
-
-
# This is a convenience method for creating a statement, binding
-
# paramters to it, and calling execute:
-
#
-
# result = db.query( "select * from foo where a=?", [5])
-
# # is the same as
-
# result = db.prepare( "select * from foo where a=?" ).execute( 5 )
-
#
-
# You must be sure to call +close+ on the ResultSet instance that is
-
# returned, or you could have problems with locks on the table. If called
-
# with a block, +close+ will be invoked implicitly when the block
-
# terminates.
-
1
def query( sql, bind_vars = [], *args )
-
-
if bind_vars.nil? || !args.empty?
-
if args.empty?
-
bind_vars = []
-
else
-
bind_vars = [bind_vars] + args
-
end
-
-
warn(<<-eowarn) if $VERBOSE
-
#{caller[0]} is calling SQLite3::Database#query with nil or multiple bind params
-
without using an array. Please switch to passing bind parameters as an array.
-
Support for this will be removed in version 2.0.0.
-
eowarn
-
end
-
-
result = prepare( sql ).execute( bind_vars )
-
if block_given?
-
begin
-
yield result
-
ensure
-
result.close
-
end
-
else
-
return result
-
end
-
end
-
-
# A convenience method for obtaining the first row of a result set, and
-
# discarding all others. It is otherwise identical to #execute.
-
#
-
# See also #get_first_value.
-
1
def get_first_row( sql, *bind_vars )
-
execute( sql, *bind_vars ).first
-
end
-
-
# A convenience method for obtaining the first value of the first row of a
-
# result set, and discarding all other values and rows. It is otherwise
-
# identical to #execute.
-
#
-
# See also #get_first_row.
-
1
def get_first_value( sql, *bind_vars )
-
execute( sql, *bind_vars ) { |row| return row[0] }
-
nil
-
end
-
-
1
alias :busy_timeout :busy_timeout=
-
-
# Creates a new function for use in SQL statements. It will be added as
-
# +name+, with the given +arity+. (For variable arity functions, use
-
# -1 for the arity.)
-
#
-
# The block should accept at least one parameter--the FunctionProxy
-
# instance that wraps this function invocation--and any other
-
# arguments it needs (up to its arity).
-
#
-
# The block does not return a value directly. Instead, it will invoke
-
# the FunctionProxy#result= method on the +func+ parameter and
-
# indicate the return value that way.
-
#
-
# Example:
-
#
-
# db.create_function( "maim", 1 ) do |func, value|
-
# if value.nil?
-
# func.result = nil
-
# else
-
# func.result = value.split(//).sort.join
-
# end
-
# end
-
#
-
# puts db.get_first_value( "select maim(name) from table" )
-
1
def create_function name, arity, text_rep=Constants::TextRep::ANY, &block
-
define_function(name) do |*args|
-
fp = FunctionProxy.new
-
block.call(fp, *args)
-
fp.result
-
end
-
self
-
end
-
-
# Creates a new aggregate function for use in SQL statements. Aggregate
-
# functions are functions that apply over every row in the result set,
-
# instead of over just a single row. (A very common aggregate function
-
# is the "count" function, for determining the number of rows that match
-
# a query.)
-
#
-
# The new function will be added as +name+, with the given +arity+. (For
-
# variable arity functions, use -1 for the arity.)
-
#
-
# The +step+ parameter must be a proc object that accepts as its first
-
# parameter a FunctionProxy instance (representing the function
-
# invocation), with any subsequent parameters (up to the function's arity).
-
# The +step+ callback will be invoked once for each row of the result set.
-
#
-
# The +finalize+ parameter must be a +proc+ object that accepts only a
-
# single parameter, the FunctionProxy instance representing the current
-
# function invocation. It should invoke FunctionProxy#result= to
-
# store the result of the function.
-
#
-
# Example:
-
#
-
# db.create_aggregate( "lengths", 1 ) do
-
# step do |func, value|
-
# func[ :total ] ||= 0
-
# func[ :total ] += ( value ? value.length : 0 )
-
# end
-
#
-
# finalize do |func|
-
# func.result = func[ :total ] || 0
-
# end
-
# end
-
#
-
# puts db.get_first_value( "select lengths(name) from table" )
-
#
-
# See also #create_aggregate_handler for a more object-oriented approach to
-
# aggregate functions.
-
1
def create_aggregate( name, arity, step=nil, finalize=nil,
-
text_rep=Constants::TextRep::ANY, &block )
-
-
factory = Class.new do
-
def self.step( &block )
-
define_method(:step, &block)
-
end
-
-
def self.finalize( &block )
-
define_method(:finalize, &block)
-
end
-
end
-
-
if block_given?
-
factory.instance_eval(&block)
-
else
-
factory.class_eval do
-
define_method(:step, step)
-
define_method(:finalize, finalize)
-
end
-
end
-
-
proxy = factory.new
-
proxy.extend(Module.new {
-
attr_accessor :ctx
-
-
def step( *args )
-
super(@ctx, *args)
-
end
-
-
def finalize
-
super(@ctx)
-
end
-
})
-
proxy.ctx = FunctionProxy.new
-
define_aggregator(name, proxy)
-
end
-
-
# This is another approach to creating an aggregate function (see
-
# #create_aggregate). Instead of explicitly specifying the name,
-
# callbacks, arity, and type, you specify a factory object
-
# (the "handler") that knows how to obtain all of that information. The
-
# handler should respond to the following messages:
-
#
-
# +arity+:: corresponds to the +arity+ parameter of #create_aggregate. This
-
# message is optional, and if the handler does not respond to it,
-
# the function will have an arity of -1.
-
# +name+:: this is the name of the function. The handler _must_ implement
-
# this message.
-
# +new+:: this must be implemented by the handler. It should return a new
-
# instance of the object that will handle a specific invocation of
-
# the function.
-
#
-
# The handler instance (the object returned by the +new+ message, described
-
# above), must respond to the following messages:
-
#
-
# +step+:: this is the method that will be called for each step of the
-
# aggregate function's evaluation. It should implement the same
-
# signature as the +step+ callback for #create_aggregate.
-
# +finalize+:: this is the method that will be called to finalize the
-
# aggregate function's evaluation. It should implement the
-
# same signature as the +finalize+ callback for
-
# #create_aggregate.
-
#
-
# Example:
-
#
-
# class LengthsAggregateHandler
-
# def self.arity; 1; end
-
#
-
# def initialize
-
# @total = 0
-
# end
-
#
-
# def step( ctx, name )
-
# @total += ( name ? name.length : 0 )
-
# end
-
#
-
# def finalize( ctx )
-
# ctx.result = @total
-
# end
-
# end
-
#
-
# db.create_aggregate_handler( LengthsAggregateHandler )
-
# puts db.get_first_value( "select lengths(name) from A" )
-
1
def create_aggregate_handler( handler )
-
proxy = Class.new do
-
def initialize handler
-
@handler = handler
-
@fp = FunctionProxy.new
-
end
-
-
def step( *args )
-
@handler.step(@fp, *args)
-
end
-
-
def finalize
-
@handler.finalize @fp
-
@fp.result
-
end
-
end
-
define_aggregator(handler.name, proxy.new(handler.new))
-
self
-
end
-
-
# Begins a new transaction. Note that nested transactions are not allowed
-
# by SQLite, so attempting to nest a transaction will result in a runtime
-
# exception.
-
#
-
# The +mode+ parameter may be either <tt>:deferred</tt> (the default),
-
# <tt>:immediate</tt>, or <tt>:exclusive</tt>.
-
#
-
# If a block is given, the database instance is yielded to it, and the
-
# transaction is committed when the block terminates. If the block
-
# raises an exception, a rollback will be performed instead. Note that if
-
# a block is given, #commit and #rollback should never be called
-
# explicitly or you'll get an error when the block terminates.
-
#
-
# If a block is not given, it is the caller's responsibility to end the
-
# transaction explicitly, either by calling #commit, or by calling
-
# #rollback.
-
1
def transaction( mode = :deferred )
-
execute "begin #{mode.to_s} transaction"
-
-
if block_given?
-
abort = false
-
begin
-
yield self
-
rescue ::Object
-
abort = true
-
raise
-
ensure
-
abort and rollback or commit
-
end
-
end
-
-
true
-
end
-
-
# Commits the current transaction. If there is no current transaction,
-
# this will cause an error to be raised. This returns +true+, in order
-
# to allow it to be used in idioms like
-
# <tt>abort? and rollback or commit</tt>.
-
1
def commit
-
execute "commit transaction"
-
true
-
end
-
-
# Rolls the current transaction back. If there is no current transaction,
-
# this will cause an error to be raised. This returns +true+, in order
-
# to allow it to be used in idioms like
-
# <tt>abort? and rollback or commit</tt>.
-
1
def rollback
-
execute "rollback transaction"
-
true
-
end
-
-
# Returns +true+ if the database has been open in readonly mode
-
# A helper to check before performing any operation
-
1
def readonly?
-
@readonly
-
end
-
-
# A helper class for dealing with custom functions (see #create_function,
-
# #create_aggregate, and #create_aggregate_handler). It encapsulates the
-
# opaque function object that represents the current invocation. It also
-
# provides more convenient access to the API functions that operate on
-
# the function object.
-
#
-
# This class will almost _always_ be instantiated indirectly, by working
-
# with the create methods mentioned above.
-
1
class FunctionProxy
-
1
attr_accessor :result
-
-
# Create a new FunctionProxy that encapsulates the given +func+ object.
-
# If context is non-nil, the functions context will be set to that. If
-
# it is non-nil, it must quack like a Hash. If it is nil, then none of
-
# the context functions will be available.
-
1
def initialize
-
@result = nil
-
@context = {}
-
end
-
-
# Set the result of the function to the given error message.
-
# The function will then return that error.
-
1
def set_error( error )
-
@driver.result_error( @func, error.to_s, -1 )
-
end
-
-
# (Only available to aggregate functions.) Returns the number of rows
-
# that the aggregate has processed so far. This will include the current
-
# row, and so will always return at least 1.
-
1
def count
-
@driver.aggregate_count( @func )
-
end
-
-
# Returns the value with the given key from the context. This is only
-
# available to aggregate functions.
-
1
def []( key )
-
@context[ key ]
-
end
-
-
# Sets the value with the given key in the context. This is only
-
# available to aggregate functions.
-
1
def []=( key, value )
-
@context[ key ] = value
-
end
-
end
-
-
1
private
-
-
1
def ordered_map_for columns, row
-
h = Hash[*columns.zip(row).flatten]
-
row.each_with_index { |r, i| h[i] = r }
-
h
-
end
-
end
-
end
-
1
require 'sqlite3/constants'
-
-
1
module SQLite3
-
1
class Exception < ::StandardError
-
1
@code = 0
-
-
# The numeric error code that this exception represents.
-
1
def self.code
-
@code
-
end
-
-
# A convenience for accessing the error code for this exception.
-
1
def code
-
self.class.code
-
end
-
end
-
-
1
class SQLException < Exception; end
-
1
class InternalException < Exception; end
-
1
class PermissionException < Exception; end
-
1
class AbortException < Exception; end
-
1
class BusyException < Exception; end
-
1
class LockedException < Exception; end
-
1
class MemoryException < Exception; end
-
1
class ReadOnlyException < Exception; end
-
1
class InterruptException < Exception; end
-
1
class IOException < Exception; end
-
1
class CorruptException < Exception; end
-
1
class NotFoundException < Exception; end
-
1
class FullException < Exception; end
-
1
class CantOpenException < Exception; end
-
1
class ProtocolException < Exception; end
-
1
class EmptyException < Exception; end
-
1
class SchemaChangedException < Exception; end
-
1
class TooBigException < Exception; end
-
1
class ConstraintException < Exception; end
-
1
class MismatchException < Exception; end
-
1
class MisuseException < Exception; end
-
1
class UnsupportedException < Exception; end
-
1
class AuthorizationException < Exception; end
-
1
class FormatException < Exception; end
-
1
class RangeException < Exception; end
-
1
class NotADatabaseException < Exception; end
-
end
-
1
require 'sqlite3/errors'
-
-
1
module SQLite3
-
-
# This module is intended for inclusion solely by the Database class. It
-
# defines convenience methods for the various pragmas supported by SQLite3.
-
#
-
# For a detailed description of these pragmas, see the SQLite3 documentation
-
# at http://sqlite.org/pragma.html.
-
1
module Pragmas
-
-
# Returns +true+ or +false+ depending on the value of the named pragma.
-
1
def get_boolean_pragma( name )
-
get_first_value( "PRAGMA #{name}" ) != "0"
-
end
-
1
private :get_boolean_pragma
-
-
# Sets the given pragma to the given boolean value. The value itself
-
# may be +true+ or +false+, or any other commonly used string or
-
# integer that represents truth.
-
1
def set_boolean_pragma( name, mode )
-
case mode
-
when String
-
case mode.downcase
-
when "on", "yes", "true", "y", "t"; mode = "'ON'"
-
when "off", "no", "false", "n", "f"; mode = "'OFF'"
-
else
-
raise Exception,
-
"unrecognized pragma parameter #{mode.inspect}"
-
end
-
when true, 1
-
mode = "ON"
-
when false, 0, nil
-
mode = "OFF"
-
else
-
raise Exception,
-
"unrecognized pragma parameter #{mode.inspect}"
-
end
-
-
execute( "PRAGMA #{name}=#{mode}" )
-
end
-
1
private :set_boolean_pragma
-
-
# Requests the given pragma (and parameters), and if the block is given,
-
# each row of the result set will be yielded to it. Otherwise, the results
-
# are returned as an array.
-
1
def get_query_pragma( name, *parms, &block ) # :yields: row
-
if parms.empty?
-
execute( "PRAGMA #{name}", &block )
-
else
-
args = "'" + parms.join("','") + "'"
-
execute( "PRAGMA #{name}( #{args} )", &block )
-
end
-
end
-
1
private :get_query_pragma
-
-
# Return the value of the given pragma.
-
1
def get_enum_pragma( name )
-
get_first_value( "PRAGMA #{name}" )
-
end
-
1
private :get_enum_pragma
-
-
# Set the value of the given pragma to +mode+. The +mode+ parameter must
-
# conform to one of the values in the given +enum+ array. Each entry in
-
# the array is another array comprised of elements in the enumeration that
-
# have duplicate values. See #synchronous, #default_synchronous,
-
# #temp_store, and #default_temp_store for usage examples.
-
1
def set_enum_pragma( name, mode, enums )
-
match = enums.find { |p| p.find { |i| i.to_s.downcase == mode.to_s.downcase } }
-
raise Exception,
-
"unrecognized #{name} #{mode.inspect}" unless match
-
execute( "PRAGMA #{name}='#{match.first.upcase}'" )
-
end
-
1
private :set_enum_pragma
-
-
# Returns the value of the given pragma as an integer.
-
1
def get_int_pragma( name )
-
get_first_value( "PRAGMA #{name}" ).to_i
-
end
-
1
private :get_int_pragma
-
-
# Set the value of the given pragma to the integer value of the +value+
-
# parameter.
-
1
def set_int_pragma( name, value )
-
execute( "PRAGMA #{name}=#{value.to_i}" )
-
end
-
1
private :set_int_pragma
-
-
# The enumeration of valid synchronous modes.
-
1
SYNCHRONOUS_MODES = [ [ 'full', 2 ], [ 'normal', 1 ], [ 'off', 0 ] ]
-
-
# The enumeration of valid temp store modes.
-
1
TEMP_STORE_MODES = [ [ 'default', 0 ], [ 'file', 1 ], [ 'memory', 2 ] ]
-
-
# Does an integrity check on the database. If the check fails, a
-
# SQLite3::Exception will be raised. Otherwise it
-
# returns silently.
-
1
def integrity_check
-
execute( "PRAGMA integrity_check" ) do |row|
-
raise Exception, row[0] if row[0] != "ok"
-
end
-
end
-
-
1
def auto_vacuum
-
get_boolean_pragma "auto_vacuum"
-
end
-
-
1
def auto_vacuum=( mode )
-
set_boolean_pragma "auto_vacuum", mode
-
end
-
-
1
def schema_cookie
-
get_int_pragma "schema_cookie"
-
end
-
-
1
def schema_cookie=( cookie )
-
set_int_pragma "schema_cookie", cookie
-
end
-
-
1
def user_cookie
-
get_int_pragma "user_cookie"
-
end
-
-
1
def user_cookie=( cookie )
-
set_int_pragma "user_cookie", cookie
-
end
-
-
1
def cache_size
-
get_int_pragma "cache_size"
-
end
-
-
1
def cache_size=( size )
-
set_int_pragma "cache_size", size
-
end
-
-
1
def default_cache_size
-
get_int_pragma "default_cache_size"
-
end
-
-
1
def default_cache_size=( size )
-
set_int_pragma "default_cache_size", size
-
end
-
-
1
def default_synchronous
-
get_enum_pragma "default_synchronous"
-
end
-
-
1
def default_synchronous=( mode )
-
set_enum_pragma "default_synchronous", mode, SYNCHRONOUS_MODES
-
end
-
-
1
def synchronous
-
get_enum_pragma "synchronous"
-
end
-
-
1
def synchronous=( mode )
-
set_enum_pragma "synchronous", mode, SYNCHRONOUS_MODES
-
end
-
-
1
def default_temp_store
-
get_enum_pragma "default_temp_store"
-
end
-
-
1
def default_temp_store=( mode )
-
set_enum_pragma "default_temp_store", mode, TEMP_STORE_MODES
-
end
-
-
1
def temp_store
-
get_enum_pragma "temp_store"
-
end
-
-
1
def temp_store=( mode )
-
set_enum_pragma "temp_store", mode, TEMP_STORE_MODES
-
end
-
-
1
def full_column_names
-
get_boolean_pragma "full_column_names"
-
end
-
-
1
def full_column_names=( mode )
-
set_boolean_pragma "full_column_names", mode
-
end
-
-
1
def parser_trace
-
get_boolean_pragma "parser_trace"
-
end
-
-
1
def parser_trace=( mode )
-
set_boolean_pragma "parser_trace", mode
-
end
-
-
1
def vdbe_trace
-
get_boolean_pragma "vdbe_trace"
-
end
-
-
1
def vdbe_trace=( mode )
-
set_boolean_pragma "vdbe_trace", mode
-
end
-
-
1
def database_list( &block ) # :yields: row
-
get_query_pragma "database_list", &block
-
end
-
-
1
def foreign_key_list( table, &block ) # :yields: row
-
get_query_pragma "foreign_key_list", table, &block
-
end
-
-
1
def index_info( index, &block ) # :yields: row
-
get_query_pragma "index_info", index, &block
-
end
-
-
1
def index_list( table, &block ) # :yields: row
-
get_query_pragma "index_list", table, &block
-
end
-
-
###
-
# Returns information about +table+. Yields each row of table information
-
# if a block is provided.
-
1
def table_info table
-
stmt = prepare "PRAGMA table_info(#{table})"
-
columns = stmt.columns
-
-
needs_tweak_default =
-
version_compare(SQLite3.libversion.to_s, "3.3.7") > 0
-
-
result = [] unless block_given?
-
stmt.each do |row|
-
new_row = Hash[columns.zip(row)]
-
-
# FIXME: This should be removed but is required for older versions
-
# of rails
-
if(Object.const_defined?(:ActiveRecord))
-
new_row['notnull'] = new_row['notnull'].to_s
-
end
-
-
tweak_default(new_row) if needs_tweak_default
-
-
if block_given?
-
yield new_row
-
else
-
result << new_row
-
end
-
end
-
stmt.close
-
-
result
-
end
-
-
1
private
-
-
# Compares two version strings
-
1
def version_compare(v1, v2)
-
v1 = v1.split(".").map { |i| i.to_i }
-
v2 = v2.split(".").map { |i| i.to_i }
-
parts = [v1.length, v2.length].max
-
v1.push 0 while v1.length < parts
-
v2.push 0 while v2.length < parts
-
v1.zip(v2).each do |a,b|
-
return -1 if a < b
-
return 1 if a > b
-
end
-
return 0
-
end
-
-
# Since SQLite 3.3.8, the table_info pragma has returned the default
-
# value of the row as a quoted SQL value. This method essentially
-
# unquotes those values.
-
1
def tweak_default(hash)
-
case hash["dflt_value"]
-
when /^null$/i
-
hash["dflt_value"] = nil
-
when /^'(.*)'$/
-
hash["dflt_value"] = $1.gsub(/''/, "'")
-
when /^"(.*)"$/
-
hash["dflt_value"] = $1.gsub(/""/, '"')
-
end
-
end
-
end
-
-
end
-
1
require 'sqlite3/constants'
-
1
require 'sqlite3/errors'
-
-
1
module SQLite3
-
-
# The ResultSet object encapsulates the enumerability of a query's output.
-
# It is a simple cursor over the data that the query returns. It will
-
# very rarely (if ever) be instantiated directly. Instead, client's should
-
# obtain a ResultSet instance via Statement#execute.
-
1
class ResultSet
-
1
include Enumerable
-
-
1
class ArrayWithTypes < Array # :nodoc:
-
1
attr_accessor :types
-
end
-
-
1
class ArrayWithTypesAndFields < Array # :nodoc:
-
1
attr_writer :types
-
1
attr_writer :fields
-
-
1
def types
-
warn(<<-eowarn) if $VERBOSE
-
#{caller[0]} is calling #{self.class}#types. This method will be removed in
-
sqlite3 version 2.0.0, please call the `types` method on the SQLite3::ResultSet
-
object that created this object
-
eowarn
-
@types
-
end
-
-
1
def fields
-
warn(<<-eowarn) if $VERBOSE
-
#{caller[0]} is calling #{self.class}#fields. This method will be removed in
-
sqlite3 version 2.0.0, please call the `columns` method on the SQLite3::ResultSet
-
object that created this object
-
eowarn
-
@fields
-
end
-
end
-
-
# The class of which we return an object in case we want a Hash as
-
# result.
-
1
class HashWithTypesAndFields < Hash # :nodoc:
-
1
attr_writer :types
-
1
attr_writer :fields
-
-
1
def types
-
warn(<<-eowarn) if $VERBOSE
-
#{caller[0]} is calling #{self.class}#types. This method will be removed in
-
sqlite3 version 2.0.0, please call the `types` method on the SQLite3::ResultSet
-
object that created this object
-
eowarn
-
@types
-
end
-
-
1
def fields
-
warn(<<-eowarn) if $VERBOSE
-
#{caller[0]} is calling #{self.class}#fields. This method will be removed in
-
sqlite3 version 2.0.0, please call the `columns` method on the SQLite3::ResultSet
-
object that created this object
-
eowarn
-
@fields
-
end
-
-
1
def [] key
-
key = fields[key] if key.is_a? Numeric
-
super key
-
end
-
end
-
-
# Create a new ResultSet attached to the given database, using the
-
# given sql text.
-
1
def initialize db, stmt
-
@db = db
-
@stmt = stmt
-
end
-
-
# Reset the cursor, so that a result set which has reached end-of-file
-
# can be rewound and reiterated.
-
1
def reset( *bind_params )
-
@stmt.reset!
-
@stmt.bind_params( *bind_params )
-
@eof = false
-
end
-
-
# Query whether the cursor has reached the end of the result set or not.
-
1
def eof?
-
@stmt.done?
-
end
-
-
# Obtain the next row from the cursor. If there are no more rows to be
-
# had, this will return +nil+. If type translation is active on the
-
# corresponding database, the values in the row will be translated
-
# according to their types.
-
#
-
# The returned value will be an array, unless Database#results_as_hash has
-
# been set to +true+, in which case the returned value will be a hash.
-
#
-
# For arrays, the column names are accessible via the +fields+ property,
-
# and the column types are accessible via the +types+ property.
-
#
-
# For hashes, the column names are the keys of the hash, and the column
-
# types are accessible via the +types+ property.
-
1
def next
-
if @db.results_as_hash
-
return next_hash
-
end
-
-
row = @stmt.step
-
return nil if @stmt.done?
-
-
if @db.type_translation
-
row = @stmt.types.zip(row).map do |type, value|
-
@db.translator.translate( type, value )
-
end
-
end
-
-
if row.respond_to?(:fields)
-
# FIXME: this can only happen if the translator returns something
-
# that responds to `fields`. Since we're removing the translator
-
# in 2.0, we can remove this branch in 2.0.
-
row = ArrayWithTypes.new(row)
-
else
-
# FIXME: the `fields` and `types` methods are deprecated on this
-
# object for version 2.0, so we can safely remove this branch
-
# as well.
-
row = ArrayWithTypesAndFields.new(row)
-
end
-
-
row.fields = @stmt.columns
-
row.types = @stmt.types
-
row
-
end
-
-
# Required by the Enumerable mixin. Provides an internal iterator over the
-
# rows of the result set.
-
1
def each
-
while node = self.next
-
yield node
-
end
-
end
-
-
# Provides an internal iterator over the rows of the result set where
-
# each row is yielded as a hash.
-
1
def each_hash
-
while node = next_hash
-
yield node
-
end
-
end
-
-
# Closes the statement that spawned this result set.
-
# <em>Use with caution!</em> Closing a result set will automatically
-
# close any other result sets that were spawned from the same statement.
-
1
def close
-
@stmt.close
-
end
-
-
# Queries whether the underlying statement has been closed or not.
-
1
def closed?
-
@stmt.closed?
-
end
-
-
# Returns the types of the columns returned by this result set.
-
1
def types
-
@stmt.types
-
end
-
-
# Returns the names of the columns returned by this result set.
-
1
def columns
-
@stmt.columns
-
end
-
-
# Return the next row as a hash
-
1
def next_hash
-
row = @stmt.step
-
return nil if @stmt.done?
-
-
# FIXME: type translation is deprecated, so this can be removed
-
# in 2.0
-
if @db.type_translation
-
row = @stmt.types.zip(row).map do |type, value|
-
@db.translator.translate( type, value )
-
end
-
end
-
-
# FIXME: this can be switched to a regular hash in 2.0
-
row = HashWithTypesAndFields[*@stmt.columns.zip(row).flatten]
-
-
# FIXME: these methods are deprecated for version 2.0, so we can remove
-
# this code in 2.0
-
row.fields = @stmt.columns
-
row.types = @stmt.types
-
row
-
end
-
end
-
end
-
1
require 'sqlite3/errors'
-
1
require 'sqlite3/resultset'
-
-
1
class String
-
1
def to_blob
-
SQLite3::Blob.new( self )
-
end
-
end
-
-
1
module SQLite3
-
# A statement represents a prepared-but-unexecuted SQL query. It will rarely
-
# (if ever) be instantiated directly by a client, and is most often obtained
-
# via the Database#prepare method.
-
1
class Statement
-
1
include Enumerable
-
-
# This is any text that followed the first valid SQL statement in the text
-
# with which the statement was initialized. If there was no trailing text,
-
# this will be the empty string.
-
1
attr_reader :remainder
-
-
# Binds the given variables to the corresponding placeholders in the SQL
-
# text.
-
#
-
# See Database#execute for a description of the valid placeholder
-
# syntaxes.
-
#
-
# Example:
-
#
-
# stmt = db.prepare( "select * from table where a=? and b=?" )
-
# stmt.bind_params( 15, "hello" )
-
#
-
# See also #execute, #bind_param, Statement#bind_param, and
-
# Statement#bind_params.
-
1
def bind_params( *bind_vars )
-
index = 1
-
bind_vars.flatten.each do |var|
-
if Hash === var
-
var.each { |key, val| bind_param key, val }
-
else
-
bind_param index, var
-
index += 1
-
end
-
end
-
end
-
-
# Execute the statement. This creates a new ResultSet object for the
-
# statement's virtual machine. If a block was given, the new ResultSet will
-
# be yielded to it; otherwise, the ResultSet will be returned.
-
#
-
# Any parameters will be bound to the statement using #bind_params.
-
#
-
# Example:
-
#
-
# stmt = db.prepare( "select * from table" )
-
# stmt.execute do |result|
-
# ...
-
# end
-
#
-
# See also #bind_params, #execute!.
-
1
def execute( *bind_vars )
-
reset! if active? || done?
-
-
bind_params(*bind_vars) unless bind_vars.empty?
-
@results = ResultSet.new(@connection, self)
-
-
step if 0 == column_count
-
-
yield @results if block_given?
-
@results
-
end
-
-
# Execute the statement. If no block was given, this returns an array of
-
# rows returned by executing the statement. Otherwise, each row will be
-
# yielded to the block.
-
#
-
# Any parameters will be bound to the statement using #bind_params.
-
#
-
# Example:
-
#
-
# stmt = db.prepare( "select * from table" )
-
# stmt.execute! do |row|
-
# ...
-
# end
-
#
-
# See also #bind_params, #execute.
-
1
def execute!( *bind_vars, &block )
-
execute(*bind_vars)
-
block_given? ? each(&block) : to_a
-
end
-
-
# Returns true if the statement is currently active, meaning it has an
-
# open result set.
-
1
def active?
-
!done?
-
end
-
-
# Return an array of the column names for this statement. Note that this
-
# may execute the statement in order to obtain the metadata; this makes it
-
# a (potentially) expensive operation.
-
1
def columns
-
9
get_metadata unless @columns
-
9
return @columns
-
end
-
-
1
def each
-
9
loop do
-
60
val = step
-
60
break self if done?
-
51
yield val
-
end
-
end
-
-
# Return an array of the data types for each column in this statement. Note
-
# that this may execute the statement in order to obtain the metadata; this
-
# makes it a (potentially) expensive operation.
-
1
def types
-
must_be_open!
-
get_metadata unless @types
-
@types
-
end
-
-
# Performs a sanity check to ensure that the statement is not
-
# closed. If it is, an exception is raised.
-
1
def must_be_open! # :nodoc:
-
if closed?
-
raise SQLite3::Exception, "cannot use a closed statement"
-
end
-
end
-
-
1
private
-
# A convenience method for obtaining the metadata about the query. Note
-
# that this will actually execute the SQL, which means it can be a
-
# (potentially) expensive operation.
-
1
def get_metadata
-
9
@columns = Array.new(column_count) do |column|
-
34
column_name column
-
end
-
9
@types = Array.new(column_count) do |column|
-
34
column_decltype column
-
end
-
end
-
end
-
end
-
1
require 'time'
-
1
require 'date'
-
-
1
module SQLite3
-
-
# The Translator class encapsulates the logic and callbacks necessary for
-
# converting string data to a value of some specified type. Every Database
-
# instance may have a Translator instance, in order to assist in type
-
# translation (Database#type_translation).
-
#
-
# Further, applications may define their own custom type translation logic
-
# by registering translator blocks with the corresponding database's
-
# translator instance (Database#translator).
-
1
class Translator
-
-
# Create a new Translator instance. It will be preinitialized with default
-
# translators for most SQL data types.
-
1
def initialize
-
@translators = Hash.new( proc { |type,value| value } )
-
@type_name_cache = {}
-
register_default_translators
-
end
-
-
# Add a new translator block, which will be invoked to process type
-
# translations to the given type. The type should be an SQL datatype, and
-
# may include parentheses (i.e., "VARCHAR(30)"). However, any parenthetical
-
# information is stripped off and discarded, so type translation decisions
-
# are made solely on the "base" type name.
-
#
-
# The translator block itself should accept two parameters, "type" and
-
# "value". In this case, the "type" is the full type name (including
-
# parentheses), so the block itself may include logic for changing how a
-
# type is translated based on the additional data. The "value" parameter
-
# is the (string) data to convert.
-
#
-
# The block should return the translated value.
-
1
def add_translator( type, &block ) # :yields: type, value
-
warn(<<-eowarn) if $VERBOSE
-
#{caller[0]} is calling `add_translator`.
-
Built in translators are deprecated and will be removed in version 2.0.0
-
eowarn
-
@translators[ type_name( type ) ] = block
-
end
-
-
# Translate the given string value to a value of the given type. In the
-
# absense of an installed translator block for the given type, the value
-
# itself is always returned. Further, +nil+ values are never translated,
-
# and are always passed straight through regardless of the type parameter.
-
1
def translate( type, value )
-
unless value.nil?
-
# FIXME: this is a hack to support Sequel
-
if type && %w{ datetime timestamp }.include?(type.downcase)
-
@translators[ type_name( type ) ].call( type, value.to_s )
-
else
-
@translators[ type_name( type ) ].call( type, value )
-
end
-
end
-
end
-
-
# A convenience method for working with type names. This returns the "base"
-
# type name, without any parenthetical data.
-
1
def type_name( type )
-
@type_name_cache[type] ||= begin
-
type = "" if type.nil?
-
type = $1 if type =~ /^(.*?)\(/
-
type.upcase
-
end
-
end
-
1
private :type_name
-
-
# Register the default translators for the current Translator instance.
-
# This includes translators for most major SQL data types.
-
1
def register_default_translators
-
[ "time",
-
"timestamp" ].each { |type| add_translator( type ) { |t, v| Time.parse( v ) } }
-
-
add_translator( "date" ) { |t,v| Date.parse(v) }
-
add_translator( "datetime" ) { |t,v| DateTime.parse(v) }
-
-
[ "decimal",
-
"float",
-
"numeric",
-
"double",
-
"real",
-
"dec",
-
"fixed" ].each { |type| add_translator( type ) { |t,v| v.to_f } }
-
-
[ "integer",
-
"smallint",
-
"mediumint",
-
"int",
-
"bigint" ].each { |type| add_translator( type ) { |t,v| v.to_i } }
-
-
[ "bit",
-
"bool",
-
"boolean" ].each do |type|
-
add_translator( type ) do |t,v|
-
!( v.strip.gsub(/00+/,"0") == "0" ||
-
v.downcase == "false" ||
-
v.downcase == "f" ||
-
v.downcase == "no" ||
-
v.downcase == "n" )
-
end
-
end
-
-
add_translator( "tinyint" ) do |type, value|
-
if type =~ /\(\s*1\s*\)/
-
value.to_i == 1
-
else
-
value.to_i
-
end
-
end
-
end
-
1
private :register_default_translators
-
-
end
-
-
end
-
1
require 'sqlite3/constants'
-
-
1
module SQLite3
-
-
1
class Value
-
1
attr_reader :handle
-
-
1
def initialize( db, handle )
-
@driver = db.driver
-
@handle = handle
-
end
-
-
1
def null?
-
type == :null
-
end
-
-
1
def to_blob
-
@driver.value_blob( @handle )
-
end
-
-
1
def length( utf16=false )
-
if utf16
-
@driver.value_bytes16( @handle )
-
else
-
@driver.value_bytes( @handle )
-
end
-
end
-
-
1
def to_f
-
@driver.value_double( @handle )
-
end
-
-
1
def to_i
-
@driver.value_int( @handle )
-
end
-
-
1
def to_int64
-
@driver.value_int64( @handle )
-
end
-
-
1
def to_s( utf16=false )
-
@driver.value_text( @handle, utf16 )
-
end
-
-
1
def type
-
case @driver.value_type( @handle )
-
when Constants::ColumnType::INTEGER then :int
-
when Constants::ColumnType::FLOAT then :float
-
when Constants::ColumnType::TEXT then :text
-
when Constants::ColumnType::BLOB then :blob
-
when Constants::ColumnType::NULL then :null
-
end
-
end
-
-
end
-
-
end
-
1
module SQLite3
-
-
1
VERSION = '1.3.6'
-
-
1
module VersionProxy
-
-
1
MAJOR = 1
-
1
MINOR = 3
-
1
TINY = 6
-
1
BUILD = nil
-
-
1
STRING = [ MAJOR, MINOR, TINY, BUILD ].compact.join( "." )
-
#:beta-tag:
-
-
1
VERSION = ::SQLite3::VERSION
-
end
-
-
1
def self.const_missing(name)
-
return super unless name == :Version
-
warn(<<-eowarn) if $VERBOSE
-
#{caller[0]}: SQLite::Version will be removed in sqlite3-ruby version 2.0.0
-
eowarn
-
VersionProxy
-
end
-
end
-
$:.unshift(File.dirname(__FILE__)) unless
-
1
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
-
-
1
module V8
-
1
require 'v8/version'
-
1
require 'v8/v8' #native glue
-
1
require 'v8/c/locker'
-
1
require 'v8/portal'
-
1
require 'v8/portal/caller'
-
1
require 'v8/portal/proxies'
-
1
require 'v8/portal/templates'
-
1
require 'v8/portal/function'
-
1
require 'v8/portal/constructor'
-
1
require 'v8/portal/interceptors'
-
1
require 'v8/context'
-
1
require 'v8/object'
-
1
require 'v8/array'
-
1
require 'v8/function'
-
1
require 'v8/tap'
-
1
require 'v8/access'
-
1
require 'v8/error'
-
1
require 'v8/stack'
-
end
-
1
require 'set'
-
1
module V8
-
1
class Access
-
1
def get(obj, name, &dontintercept)
-
methods = accessible_methods(obj)
-
if methods.include?(name)
-
method = obj.method(name)
-
method.arity == 0 ? method.call : method.unbind
-
elsif obj.respond_to?(:[])
-
obj.send(:[], name, &dontintercept)
-
else
-
yield
-
end
-
end
-
-
1
def iget(obj, index, &dontintercept)
-
if obj.respond_to?(:[])
-
obj.send(:[], index, &dontintercept)
-
else
-
yield
-
end
-
end
-
-
1
def set(obj, name, value, &dontintercept)
-
setter = name + "="
-
methods = accessible_methods(obj, true)
-
if methods.include?(setter)
-
obj.send(setter, value)
-
elsif obj.respond_to?(:[]=)
-
obj.send(:[]=, name, value, &dontintercept)
-
else
-
yield
-
end
-
end
-
-
1
def iset(obj, index, value, &dontintercept)
-
if obj.respond_to?(:[]=)
-
obj.send(:[]=, index, value, &dontintercept)
-
else
-
yield
-
end
-
end
-
-
1
def query(obj, name, attributes)
-
if obj.respond_to?(name)
-
attributes.dont_delete
-
unless obj.respond_to?(name + "=")
-
attributes.read_only
-
end
-
else
-
yield
-
end
-
end
-
-
1
def iquery(obj, index, attributes)
-
if obj.respond_to?(:[])
-
attributes.dont_delete
-
unless obj.respond_to?(:[]=)
-
attributes.read_only
-
end
-
else
-
yield
-
end
-
end
-
-
1
def names(obj)
-
accessible_methods(obj)
-
end
-
-
1
def indices(obj)
-
obj.respond_to?(:length) ? (0..obj.length).to_a : yield
-
end
-
-
1
private
-
-
1
def accessible_methods(obj, special_methods = false)
-
obj.public_methods(false).map {|m| m.to_s}.to_set.tap do |methods|
-
ancestors = obj.class.ancestors.dup
-
while ancestor = ancestors.shift
-
break if ancestor == ::Object
-
methods.merge(ancestor.public_instance_methods(false).map {|m| m.to_s})
-
end
-
methods.reject! {|m| m == "[]" || m == "[]=" || m =~ /=$/} unless special_methods
-
end
-
end
-
end
-
end
-
-
1
module V8
-
1
class Array < V8::Object
-
-
1
def each
-
@portal.open do |to|
-
for i in 0..(@native.Length() - 1)
-
yield to.rb(@native.Get(i))
-
end
-
end
-
end
-
-
1
def length
-
@native.Length()
-
end
-
end
-
end
-
1
module V8
-
1
module C
-
# Shim to support the old style locking syntax. We don't currently
-
# deprecate this because it might make a comeback at some point.
-
#
-
# to synchronize access to V8, and to associate V8 with this thread:
-
#
-
# Locker() do
-
# #... interact with v8
-
# end
-
1
def self.Locker
-
lock = Locker.new
-
yield
-
ensure
-
lock.delete
-
end
-
end
-
end
-
1
require 'stringio'
-
-
1
module V8
-
1
class Context
-
1
attr_reader :native, :scope, :access
-
-
1
def initialize(opts = {})
-
@access = Access.new
-
@to = Portal.new(self, @access)
-
@to.lock do
-
with = opts[:with]
-
constructor = nil
-
template = if with
-
constructor = @to.templates.to_constructor(with.class)
-
constructor.disable()
-
constructor.template.InstanceTemplate()
-
else
-
C::ObjectTemplate::New()
-
end
-
@native = opts[:with] ? C::Context::New(template) : C::Context::New()
-
@native.enter do
-
@global = @native.Global()
-
@to.proxies.register_javascript_proxy @global, :for => with if with
-
constructor.enable() if constructor
-
@scope = @to.rb(@global)
-
@global.SetHiddenValue(C::String::NewSymbol("TheRubyRacer::RubyContext"), C::External::New(self))
-
end
-
yield(self) if block_given?
-
end
-
end
-
-
1
def eval(javascript, filename = "<eval>", line = 1)
-
if IO === javascript || StringIO === javascript
-
javascript = javascript.read()
-
end
-
err = nil
-
value = nil
-
@to.lock do
-
C::TryCatch.try do |try|
-
@native.enter do
-
script = C::Script::Compile(@to.v8(javascript.to_s), @to.v8(filename.to_s))
-
if try.HasCaught()
-
err = JSError.new(try, @to)
-
else
-
result = script.Run()
-
if try.HasCaught()
-
err = JSError.new(try, @to)
-
else
-
value = @to.rb(result)
-
end
-
end
-
end
-
end
-
end
-
raise err if err
-
return value
-
end
-
-
1
def load(filename)
-
File.open(filename) do |file|
-
self.eval file, filename, 1
-
end
-
end
-
-
1
def [](key)
-
@to.open do
-
@to.rb @global.Get(@to.v8(key))
-
end
-
end
-
-
1
def []=(key, value)
-
@to.open do
-
@global.Set(@to.v8(key), @to.v8(value))
-
end
-
return value
-
end
-
-
1
def self.stack(limit = 99)
-
if native = C::Context::GetEntered()
-
global = native.Global()
-
cxt = global.GetHiddenValue(C::String::NewSymbol("TheRubyRacer::RubyContext")).Value()
-
cxt.instance_eval {@to.rb(C::StackTrace::CurrentStackTrace(limit))}
-
else
-
[]
-
end
-
end
-
-
1
def self.passes_this_argument?
-
true
-
end
-
end
-
-
1
module C
-
1
class Context
-
1
def enter
-
if block_given?
-
if IsEntered()
-
yield(self)
-
else
-
Enter()
-
begin
-
yield(self)
-
ensure
-
Exit()
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
-
1
module V8
-
1
class JSError < StandardError
-
1
attr_reader :value, :boundaries
-
-
1
def initialize(try, to)
-
@to = to
-
super(initialize_unsafe(try))
-
rescue Exception => e
-
@boundaries = [Boundary.new(:rbframes => e.backtrace)]
-
@value = e
-
super(<<-EOMSG)
-
You have uncovered what is probably a BUG in therubyracer exception code. An error report would be very helpful.
-
JSError#initialize failed!: #{e.message}"
-
EOMSG
-
end
-
-
1
def initialize_unsafe(try)
-
message = nil
-
ex = @to.rb(try.Exception())
-
@boundaries = [Boundary.new(:rbframes => caller(3), :jsframes => parse_js_frames(try))]
-
if V8::Object === ex
-
if msg = ex['message']
-
message = msg
-
else
-
message = ex.to_s
-
end
-
if cause = ex.instance_variable_get(:@native).GetHiddenValue("TheRubyRacer::Cause")
-
if !cause.IsEmpty()
-
prev = cause.Value()
-
if prev.kind_of?(JSError)
-
@boundaries.concat prev.boundaries
-
@value = prev.value
-
else
-
@value = prev
-
@boundaries.concat [Boundary.new(:rbframes => prev.backtrace)]
-
end
-
else
-
@value = ex
-
end
-
end
-
else
-
@value = ex
-
message = ex.to_s
-
end
-
return message
-
end
-
-
1
def in_ruby?
-
@value.kind_of?(Exception)
-
end
-
-
1
def in_javascript?
-
!in_ruby?
-
end
-
-
1
def backtrace(*modifiers)
-
trace_framework = modifiers.include?(:framework)
-
trace_ruby = modifiers.length == 0 || modifiers.include?(:ruby)
-
trace_javascript = modifiers.length == 0 || modifiers.include?(:javascript)
-
mixed = []
-
rbcontext = []
-
jscontext = []
-
@boundaries.each do |b|
-
rbframes = b.rbframes.dup
-
rbcontext.reverse_each do |frame|
-
if frame == rbframes.last
-
rbframes.pop
-
else
-
break
-
end
-
end if trace_ruby
-
jsframes = b.jsframes.dup
-
jscontext.reverse_each do |frame|
-
if frame == jsframes.last
-
jsframes.pop
-
else
-
break
-
end
-
end if trace_javascript
-
rbcontext = b.rbframes
-
jscontext = b.jsframes
-
rbframes.reject! {|f| f =~ /lib\/v8\/.*\.rb/} unless trace_framework
-
mixed.unshift(*rbframes) if trace_ruby
-
mixed.unshift(*jsframes) if trace_javascript
-
end
-
return mixed
-
end
-
-
1
def parse_js_frames(try)
-
#I can't figure out why V8 is not capturing the stacktrace here
-
#in terms of StackTrace and StackFrame objects, so we have to
-
#parse the string.
-
raw = @to.rb(try.StackTrace())
-
if raw && !raw.empty? && !syntax_error?(try)
-
raw.split("\n")[1..-1].tap do |frames|
-
frames.each {|frame| frame.strip!; frame.chomp!(",")}
-
end.reject(&:empty?)
-
else
-
msg = try.Message()
-
["at #{@to.rb(msg.GetScriptResourceName())}:#{msg.GetLineNumber()}:#{msg.GetStartColumn() + 1}"]
-
end
-
end
-
-
#Syntax errors are weird in that they have a non-empty stack trace
-
#but it does not contain any source location information, so
-
#in these instances, we have to pull it out of the Message object
-
#in the TryCatch. Is there a better way to detect a syntax error
-
1
def syntax_error?(try)
-
ex = @to.rb(try.Exception())
-
if ex && ex.kind_of?(V8::Object)
-
type = ex["constructor"]
-
type && type.kind_of?(V8::Function) && type.name == "SyntaxError"
-
else
-
false
-
end
-
end
-
-
1
class Boundary
-
1
attr_reader :rbframes, :jsframes
-
-
1
def initialize(frames = {})
-
@rbframes = frames[:rbframes] || []
-
@jsframes = frames[:jsframes] || []
-
end
-
end
-
end
-
#deprecated -- use JSError
-
1
JavasriptError = JSError
-
end
-
1
module V8
-
1
class Function < V8::Object
-
-
1
def methodcall(thisObject, *args)
-
err = nil
-
return_value = nil
-
@portal.open do |to|
-
C::TryCatch.try do |try|
-
this = to.v8(thisObject)
-
return_value = to.rb(@native.Call(this, to.v8(args)))
-
err = JSError.new(try, to) if try.HasCaught()
-
end
-
end
-
raise err if err
-
return return_value
-
end
-
-
1
def call(*args)
-
@portal.open do
-
self.methodcall(@portal.context.native.Global(), *args)
-
end
-
end
-
-
1
def new(*args)
-
@portal.open do |to|
-
to.rb(@native.NewInstance(to.v8(args)))
-
end
-
end
-
-
1
def name
-
@portal.open do |to|
-
to.rb(@native.GetName())
-
end
-
end
-
-
1
def name=(name)
-
name.tap do
-
@portal.open do |to|
-
@native.SetName(to.v8(name))
-
end
-
end
-
end
-
end
-
end
-
-
1
module V8
-
1
class Object
-
1
include Enumerable
-
-
1
def initialize(native, portal)
-
@native, @portal = native, portal
-
end
-
-
1
def [](key)
-
@portal.open do |to|
-
to.rb(@native.Get(to.v8(key)))
-
end
-
end
-
-
1
def []=(key, value)
-
value.tap do
-
@portal.open do |to|
-
@native.Set(to.v8(key), to.v8(value))
-
end
-
end
-
end
-
-
1
def to_s
-
@portal.open do |to|
-
to.rb(@native.ToString())
-
end
-
end
-
-
1
def each
-
@portal.open do |to|
-
for prop in to.rb(@native.GetPropertyNames())
-
yield prop, self[prop]
-
end
-
end
-
end
-
-
1
def respond_to?(method)
-
super or self[method] != nil
-
end
-
-
1
def method_missing(name, *args, &block)
-
if name.to_s =~ /(.*)=$/
-
if args.length > 1
-
self[$1] = args
-
return args
-
else
-
self[$1] = args.first
-
return args
-
end
-
end
-
return super(name, *args, &block) unless self.respond_to?(name)
-
property = self[name]
-
if property.kind_of?(V8::Function)
-
property.methodcall(self, *args)
-
elsif args.empty?
-
property
-
else
-
raise ArgumentError, "wrong number of arguments (#{args.length} for 0)" unless args.empty?
-
end
-
end
-
end
-
end
-
-
1
class Object
-
1
def eval_js(javascript)
-
V8::Context.new(:with => self).eval(javascript)
-
end
-
end
-
-
1
module V8
-
1
class Portal
-
1
attr_reader :context, :access, :proxies, :templates, :interceptors, :caller
-
-
1
def initialize(context, access)
-
@context, @access = context, access
-
@proxies = Proxies.new
-
@templates = Templates.new(self)
-
@interceptors = Interceptors.new(self)
-
@caller = Caller.new(self)
-
end
-
-
1
def lock
-
lock = V8::C::Locker.new
-
yield
-
ensure
-
lock.delete
-
end
-
-
1
def open
-
lock do
-
@context.native.enter do
-
yield(self)
-
end if block_given?
-
end
-
end
-
-
1
def rb(value)
-
@proxies.js2rb(value) do
-
case value
-
when V8::C::Function then V8::Function.new(value, self)
-
when V8::C::Array then V8::Array.new(value, self)
-
when V8::C::Object then V8::Object.new(value, self)
-
when V8::C::String then value.Utf8Value.tap {|s| return s.respond_to?(:force_encoding) ? s.force_encoding("UTF-8") : s}
-
when V8::C::Date then Time.at(value.NumberValue() / 1000)
-
when V8::C::StackTrace then V8::StackTrace.new(self, value)
-
when V8::C::Value then nil if value.IsEmpty()
-
else
-
value
-
end
-
end
-
end
-
-
1
def v8(value)
-
case value
-
when V8::Object
-
value.instance_eval {@native}
-
when String
-
C::String::New(value)
-
when Symbol
-
C::String::NewSymbol(value.to_s)
-
when Proc,Method,UnboundMethod
-
@proxies.rb2js(value) do
-
@templates.to_function(value).function
-
end
-
when ::Array
-
C::Array::New(value.length).tap do |a|
-
value.each_with_index do |item, i|
-
a.Set(i, v8(item))
-
end
-
end
-
when ::Hash
-
C::Object::New().tap do |o|
-
value.each do |key, val|
-
o.Set(v8(key), v8(val))
-
end
-
end
-
when ::Time
-
C::Date::New(value.to_f * 1000)
-
when ::Class
-
@proxies.rb2js(value) do
-
constructor = @templates.to_constructor(value)
-
constructor.exposed = true
-
constructor.function
-
end
-
when nil,Numeric,TrueClass,FalseClass, C::Value
-
value
-
else
-
@proxies.rb2js(value) do
-
@templates.to_constructor(value.class).allocate(value)
-
end
-
end
-
end
-
end
-
end
-
-
1
module V8
-
1
class Portal
-
1
class Caller
-
-
1
def initialize(portal)
-
@portal = portal
-
end
-
-
1
def raw
-
yield
-
rescue Exception => e
-
case e
-
when SystemExit, NoMemoryError
-
raise e
-
else
-
error = V8::C::Exception::Error(V8::C::String::New(e.message))
-
#TODO: This is almost certainly a crash here.
-
#we need to hold onto `error` while it bubbles up the javascript stack.
-
error.SetHiddenValue("TheRubyRacer::Cause", C::External::New(e))
-
V8::C::ThrowException(error)
-
end
-
end
-
-
1
def protect(*args, &block)
-
@portal.v8 raw(*args, &block)
-
end
-
-
1
def invoke(code, *args, &block)
-
protect do
-
args = args.slice(0, code.arity) if code.arity >= 0
-
code.call(*args, &block)
-
end
-
end
-
end
-
end
-
end
-
-
1
module V8
-
1
class Portal
-
1
class ConstructorAdapter
-
1
attr_reader :template, :exposed
-
1
alias_method :exposed?, :exposed
-
-
1
def initialize(templates, class_id)
-
@exposed = false
-
@class_id = class_id
-
@templates = templates
-
@invoke = method(:invoke)
-
@template = C::FunctionTemplate::New(@invoke)
-
portal.interceptors.setup(@template.InstanceTemplate())
-
cls = self.ruby_class
-
superclass = cls.superclass
-
if cls != ::Object && superclass != ::Object && superclass != ::Class
-
@template.Inherit(templates.to_constructor(superclass).template)
-
end
-
if cls.name && cls.name =~ /(::)?(\w+?)$/
-
template.SetClassName(C::String::NewSymbol("rb::" + $2))
-
else
-
template.SetClassName("Ruby")
-
end
-
end
-
-
1
def function
-
@template.GetFunction()
-
end
-
-
1
def allocate(object)
-
arguments = C::Array::New(1)
-
arguments.Set(0, C::External::New(object))
-
function.NewInstance(arguments)
-
end
-
-
1
def disable
-
@disabled = true
-
end
-
-
1
def enable
-
@disabled = nil
-
end
-
-
1
def invoke(arguments)
-
return if @disabled
-
if !@exposed
-
unless arguments.Length() == 1 && arguments[0].kind_of?(C::External)
-
C::ThrowException(C::Exception::Error(C::String::New("cannot call native constructor from javascript")))
-
else
-
object = arguments[0].Value()
-
proxies.register_javascript_proxy arguments.This(), :for => object
-
end
-
else
-
instance = nil
-
if arguments.Length() > 0 && arguments[0].kind_of?(C::External)
-
instance = arguments[0].Value()
-
else
-
rbargs = []
-
for i in 0..arguments.Length() - 1
-
rbargs << @templates.portal.rb(arguments[i])
-
end
-
instance = portal.caller.raw do
-
self.ruby_class.new(*rbargs)
-
end
-
end
-
proxies.register_javascript_proxy arguments.This(), :for => instance
-
end
-
end
-
-
1
def exposed=(exposed)
-
if exposed && !@augmented
-
#create a prototype so that this constructor also acts like a ruby object
-
prototype_template = C::ObjectTemplate::New()
-
portal.interceptors.setup(prototype_template)
-
prototype = prototype_template.NewInstance()
-
#set *that* object's prototype to an empty function so that it will look and behave like a function.
-
prototype.SetPrototype(C::FunctionTemplate::New().GetFunction())
-
template.GetFunction().SetPrototype(prototype)
-
@augmented = true
-
end
-
@exposed = exposed
-
end
-
-
1
def ruby_class
-
ObjectSpace._id2ref(@class_id)
-
end
-
-
1
def proxies
-
@templates.proxies
-
end
-
-
1
def portal
-
@templates.portal
-
end
-
end
-
end
-
end
-
1
module V8
-
1
class Portal
-
1
class FunctionAdapter
-
-
1
attr_reader :template
-
-
1
def initialize(portal, code)
-
@portal = portal
-
@caller = case code
-
when Method then BoundCall.new(portal)
-
when UnboundMethod then BindAndCall.new(portal)
-
else Call.new(portal)
-
end
-
@code = code
-
@template = V8::C::FunctionTemplate::New(@caller, @code)
-
end
-
-
1
def function
-
@template.GetFunction()
-
end
-
-
1
class Call
-
1
def initialize(portal)
-
@portal = portal
-
end
-
-
1
def call(arguments)
-
proc = arguments.Data()
-
rbargs = [@portal.rb(arguments.This())]
-
for i in 0..arguments.Length() - 1
-
rbargs << @portal.rb(arguments[i])
-
end
-
@portal.caller.invoke(proc, *rbargs)
-
end
-
end
-
-
1
class BoundCall < Call
-
1
def call(arguments)
-
proc = arguments.Data()
-
rbargs = []
-
for i in 0..arguments.Length() - 1
-
rbargs << @portal.rb(arguments[i])
-
end
-
@portal.caller.invoke(proc, *rbargs)
-
end
-
end
-
-
1
class BindAndCall < Call
-
1
def call(arguments)
-
method = arguments.Data()
-
rbargs = []
-
for i in 0..arguments.Length() - 1
-
rbargs << @portal.rb(arguments[i])
-
end
-
this = @portal.rb(arguments.This())
-
@portal.caller.protect do
-
method.bind(this).call(*rbargs)
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module V8
-
1
class Portal
-
1
class Interceptors
-
1
def initialize(portal)
-
@getter = NamedPropertyGetter.new(portal)
-
@setter = NamedPropertySetter.new(portal)
-
@query = nil
-
@deleter = nil
-
@enumerator = NamedPropertyEnumerator.new(portal)
-
@igetter = IndexedPropertyGetter.new(portal)
-
@isetter = IndexedPropertySetter.new(portal)
-
@iquery = nil
-
@ideleter = nil
-
@ienumerator = IndexedPropertyEnumerator.new(portal)
-
end
-
-
1
def setup(template)
-
template.SetNamedPropertyHandler(@getter,@setter,@query,@deleter,@enumerator, nil)
-
template.SetIndexedPropertyHandler(@igetter,@isetter,@iquery,@ideleter,@ienumerator, nil)
-
end
-
-
1
class PropertyAttributes
-
1
attr_reader :flags
-
1
def initialize
-
@flags = 0
-
end
-
-
1
def read_only
-
tap do
-
@flags |= V8::C::ReadOnly
-
end
-
end
-
-
1
def dont_enum
-
tap do
-
@flags |= V8::C::DontEnum
-
end
-
end
-
-
1
def dont_delete
-
tap do
-
@flags |= V8::C::DontDelete
-
end
-
end
-
end
-
-
1
class Interceptor
-
-
1
def initialize(portal)
-
@to, @access = portal, portal.access
-
end
-
-
1
def intercept(info, retval = nil, &code)
-
obj = @to.rb(info.This())
-
intercepts = true
-
result = @to.caller.protect do
-
dontintercept = proc do
-
intercepts = false
-
end
-
code.call(obj, dontintercept)
-
end
-
intercepts ? (retval || result) : C::Empty
-
end
-
end
-
-
1
class NamedPropertyGetter < Interceptor
-
1
def call(property, info)
-
intercept(info) do |obj, dontintercept|
-
@access.get(obj, @to.rb(property), &dontintercept)
-
end
-
end
-
end
-
-
1
class NamedPropertySetter < Interceptor
-
1
def call(property, value, info)
-
intercept(info, value) do |obj, dontintercept|
-
@access.set(obj, @to.rb(property), @to.rb(value), &dontintercept)
-
end
-
end
-
end
-
-
1
class NamedPropertyQuery
-
1
def call(property, info)
-
attributes = PropertyAttributes.new
-
result = intercept(info) do |obj, dontintercept|
-
@access.query(obj, @to.rb(property), attributes, &dontintercept)
-
end
-
return result == C::Empty ? result : C::Integer::New(attributes.flags)
-
end
-
end
-
-
1
class NamedPropertyEnumerator < Interceptor
-
1
def call(info)
-
intercept(info) do |obj, dontintercept|
-
@access.names(obj, &dontintercept).to_a
-
end
-
end
-
end
-
-
1
class NamedPropertyDeleter < Interceptor
-
1
def call(property, info)
-
intercept(info) do |obj, dontintercept|
-
@access.delete(obj, property, &dontintercept)
-
end
-
end
-
end
-
-
1
class IndexedPropertyGetter < Interceptor
-
1
def call(index, info)
-
intercept(info) do |obj, dontintercept|
-
@access.iget(obj, index, &dontintercept)
-
end
-
end
-
end
-
-
1
class IndexedPropertySetter < Interceptor
-
1
def call(index, value, info)
-
intercept(info, value) do |obj, dontintercept|
-
@access.iset(obj, index, @to.rb(value), &dontintercept)
-
end
-
end
-
end
-
-
1
class IndexedPropertyQuery < Interceptor
-
1
def call(property, info)
-
attributes = PropertyAttributes.new
-
result = intercept(info) do |obj, dontintercept|
-
@access.indices(obj, &dontintercept)
-
end
-
result == C::Empty ? C::Empty : C::Integer::New(attributes.flags)
-
end
-
end
-
-
1
class IndexedPropertyDeleter < Interceptor
-
1
def call(index, info)
-
intercept(info) do |obj, dontintercept|
-
@access.idelete(obj, index, &dontintercept)
-
end
-
end
-
end
-
-
1
class IndexedPropertyEnumerator < Interceptor
-
1
def call(info)
-
intercept(info) do |obj, dontintercept|
-
@access.indices(obj, &dontintercept)
-
end
-
end
-
end
-
end
-
end
-
end
-
-
1
module V8
-
1
class Portal
-
1
class Proxies
-
-
1
def initialize
-
@js_proxies_rb2js = {}
-
@js_proxies_js2rb = {}
-
@rb_proxies_rb2js = {}
-
@rb_proxies_js2rb = {}
-
@clear_js_proxy = ClearJSProxy.new(@js_proxies_rb2js, @js_proxies_js2rb)
-
@clear_rb_proxy = ClearRubyProxy.new(@rb_proxies_rb2js, @rb_proxies_js2rb)
-
end
-
-
1
def js2rb(js)
-
if rb = js_proxy_2_rb_object(js)
-
return rb
-
elsif rb = js_object_2_rb_proxy(js)
-
return rb
-
else
-
proxy = block_given? ? yield(js) : Object.new
-
register_ruby_proxy proxy, :for => js if proxy && js && js.kind_of?(V8::C::Handle)
-
return proxy
-
end
-
end
-
-
1
def rb2js(rb)
-
if js = rb_proxy_2_js_object(rb)
-
return js
-
elsif js = rb_object_2_js_proxy(rb)
-
return js
-
else
-
proxy = block_given? ? yield(rb) : V8::C::Object::New()
-
register_javascript_proxy proxy, :for => rb unless @js_proxies_rb2js[rb]
-
return proxy
-
end
-
end
-
-
1
def register_javascript_proxy(proxy, options = {})
-
target = options[:for] or fail ArgumentError, "must specify the object that you're proxying with the :for => param"
-
fail ArgumentError, "javascript proxy must be a V8::C::Handle, not #{proxy.class}" unless proxy.kind_of?(V8::C::Handle)
-
fail DoubleProxyError, "target already has a registered proxy" if @js_proxies_rb2js[target]
-
-
@js_proxies_js2rb[proxy] = target
-
@js_proxies_rb2js[target] = proxy
-
proxy.MakeWeak(nil, @clear_js_proxy)
-
end
-
-
# Lookup the JavaScript proxy for a natively Ruby object
-
# @param [Object] object the Ruby object
-
# @return [V8::C::Handle] the JavaScript proxy representing `object`
-
1
def rb_object_2_js_proxy(object)
-
@js_proxies_rb2js[object]
-
end
-
-
# Look up a natively Ruby object given its JavaScript proxy
-
# @param [V8::C::Handle] proxy the JavaScript proxy
-
# @return [Object] the Ruby object represented by `proxy`
-
1
def js_proxy_2_rb_object(proxy)
-
@js_proxies_js2rb[proxy]
-
end
-
-
1
def register_ruby_proxy(proxy, options = {})
-
target = options[:for] or fail ArgumentError, "must specify the object that you're proxying with the :for => param"
-
fail ArgumentError, "'#{proxy.inspect}' is not a Handle to an actual V8 object" unless target.kind_of?(V8::C::Handle)
-
@rb_proxies_rb2js[proxy.object_id] = target
-
@rb_proxies_js2rb[target] = proxy.object_id
-
ObjectSpace.define_finalizer(proxy, @clear_rb_proxy)
-
end
-
-
# Looks up the Ruby proxy for an object that is natively JavaScript
-
# @param [V8::C::Handle] object the JavaScript whose proxy we want
-
# @return [Object] the Ruby proxy for `object`
-
1
def js_object_2_rb_proxy(object)
-
if id = @rb_proxies_js2rb[object]
-
ObjectSpace._id2ref id
-
end
-
rescue RangeError
-
# sometimes, the Ruby proxy has been garbage collected, but
-
# the finalizer which runs has not been called. That's OK
-
# we just clear out the entry, and return nil so that a new
-
# proxy can be created.
-
@clear_rb_proxy.call(id)
-
return nil
-
end
-
-
# Looks up a native JavaScript object by its Ruby proxy
-
# @param [Object] proxy the Ruby proxy
-
# @return [V8::C::Handle] the native JavaScript object
-
1
def rb_proxy_2_js_object(proxy)
-
@rb_proxies_rb2js[proxy.object_id]
-
end
-
-
1
def js_empty?
-
@js_proxies_rb2js.empty? && @js_proxies_js2rb.empty?
-
end
-
-
1
def rb_empty?
-
@rb_proxies_rb2js.empty? && @rb_proxies_js2rb.empty?
-
end
-
-
1
def empty?
-
js_empty? && rb_empty?
-
end
-
1
DoubleProxyError = Class.new(StandardError)
-
-
1
class ClearJSProxy
-
-
1
def initialize(rb2js, js2rb)
-
@rb2js, @js2rb = rb2js, js2rb
-
end
-
-
1
def call(proxy, parameter)
-
rb = @js2rb[proxy]
-
@js2rb.delete(proxy)
-
@rb2js.delete(rb)
-
end
-
end
-
-
# @Private
-
# Remove the linkage between a Ruby proxy and a native
-
# JavaScript object. In general, this object is registered
-
# as a finalizer on the Ruby proxy itself, so that when it is
-
# garbage collected, it releases the back reference to the
-
# native JavaScript object.
-
#
-
# It is important to do this as soon as is reasonably possible
-
# so that the native JavaScript object can itself be garbage
-
# collected (provided there are no other references to it)
-
1
class ClearRubyProxy
-
1
def initialize(rb2js, js2rb)
-
@rb2js, @js2rb = rb2js, js2rb
-
end
-
-
# takes the object id of a Ruby proxy that has been garbage collected
-
# and releases the reference to the native JavaScript object that
-
# it was bound to.
-
# @param[Fixnum] proxy_id the proxy id of the garbage collected Ruby proxy
-
1
def call(proxy_id)
-
# TODO: this if-check should be synchronized, so that if called manually
-
# it will not conflict with the finalization thread. It's not so heinous
-
# if the refererence gets cleared twice, but we definiteily dont't want
-
# to double-decrement the v8 GC hint.
-
if js = @rb2js[proxy_id]
-
@rb2js.delete(proxy_id)
-
@js2rb.delete(js)
-
end
-
end
-
end
-
end
-
end
-
end
-
-
1
module V8
-
1
class Portal
-
1
class Templates
-
-
1
attr_reader :portal
-
-
1
def initialize(portal)
-
@portal = portal
-
@constructors = {}
-
@methods = {}
-
@procs = {}
-
@releases = {}
-
end
-
-
1
def to_constructor(ruby_class)
-
class_id = ruby_class.object_id
-
if constructor = @constructors[class_id]
-
return constructor
-
else
-
constructor = @constructors[class_id] = ConstructorAdapter.new(self, class_id)
-
ObjectSpace.define_finalizer(ruby_class, release(@constructors, class_id))
-
return constructor
-
end
-
end
-
-
1
def to_function(code)
-
case code
-
when Method, UnboundMethod
-
if fn = @methods[code.to_s]
-
return fn
-
else
-
function = @methods[code.to_s] = FunctionAdapter.new(@portal, code)
-
#TODO: test this weak behavior
-
function.template.MakeWeak(0, release(@methods, code.to_s))
-
return function
-
end
-
else
-
if fn = @procs[code]
-
return fn
-
else
-
function = @procs[code] = FunctionAdapter.new(@portal, code)
-
#TODO: test this weak behavior
-
function.template.MakeWeak(0, release(@procs, code))
-
return function
-
end
-
end
-
end
-
-
1
def proxies
-
@portal.proxies
-
end
-
-
1
def release(refs, id)
-
release = Release.new(@releases, refs, id)
-
@releases[release] = true
-
return release
-
end
-
-
1
class Release
-
1
def initialize(releases, refs, id)
-
@releases, @refs, @id = releases, refs, id
-
end
-
-
1
def call(*args)
-
@refs.delete(@id)
-
@releases.delete(self)
-
end
-
-
end
-
end
-
end
-
end
-
-
1
module V8
-
-
1
class StackTrace
-
1
include Enumerable
-
-
1
def initialize(to, native)
-
@to = to
-
@native = native
-
end
-
-
1
def length
-
@native.GetFrameCount()
-
end
-
-
1
def each
-
for i in 0..length - 1
-
yield V8::StackFrame.new(@to, @native.GetFrame(i))
-
end
-
end
-
-
1
def to_s
-
map {|f|"at #{f}"}.join("\n")
-
end
-
end
-
-
1
class StackFrame
-
-
1
def initialize(portal, native)
-
@to = portal
-
@native = native
-
end
-
-
1
def script_name
-
@to.rb(@native.GetScriptName())
-
end
-
-
1
def function_name
-
@to.rb(@native.GetFunctionName())
-
end
-
-
1
def line_number
-
@native.GetLineNumber()
-
end
-
-
1
def column
-
@native.GetColumn()
-
end
-
-
1
def eval?
-
@native.IsEval()
-
end
-
-
1
def constructor?
-
@native.IsConstructor()
-
end
-
-
1
def to_s
-
if @native.GetFunctionName()
-
"#{function_name} (#{script_name}:#{line_number}:#{column})"
-
else
-
"#{script_name}:#{line_number}:#{column}"
-
end
-
end
-
end
-
end
-
-
1
unless Object.method_defined?(:tap)
-
class Object
-
def tap
-
yield self
-
return self
-
end
-
end
-
end
-
1
module V8
-
1
VERSION = "0.10.1"
-
end
-
1
module Tilt
-
1
VERSION = '1.3.3'
-
-
1
@preferred_mappings = Hash.new
-
25
@template_mappings = Hash.new { |h, k| h[k] = [] }
-
-
# Hash of template path pattern => template implementation class mappings.
-
1
def self.mappings
-
39
@template_mappings
-
end
-
-
1
def self.normalize(ext)
-
39
ext.to_s.downcase.sub(/^\./, '')
-
end
-
-
# Register a template implementation by file extension.
-
1
def self.register(template_class, *extensions)
-
23
if template_class.respond_to?(:to_str)
-
# Support register(ext, template_class) too
-
extensions, template_class = [template_class], extensions[0]
-
end
-
-
23
extensions.each do |ext|
-
39
ext = normalize(ext)
-
39
mappings[ext].unshift(template_class).uniq!
-
end
-
end
-
-
# Makes a template class preferred for the given file extensions. If you
-
# don't provide any extensions, it will be preferred for all its already
-
# registered extensions:
-
#
-
# # Prefer RDiscount for its registered file extensions:
-
# Tilt.prefer(Tilt::RDiscountTemplate)
-
#
-
# # Prefer RDiscount only for the .md extensions:
-
# Tilt.prefer(Tilt::RDiscountTemplate, '.md')
-
1
def self.prefer(template_class, *extensions)
-
if extensions.empty?
-
mappings.each do |ext, klasses|
-
@preferred_mappings[ext] = template_class if klasses.include? template_class
-
end
-
else
-
extensions.each do |ext|
-
ext = normalize(ext)
-
register(template_class, ext)
-
@preferred_mappings[ext] = template_class
-
end
-
end
-
end
-
-
# Returns true when a template exists on an exact match of the provided file extension
-
1
def self.registered?(ext)
-
mappings.key?(ext.downcase) && !mappings[ext.downcase].empty?
-
end
-
-
# Create a new template for the given file using the file's extension
-
# to determine the the template mapping.
-
1
def self.new(file, line=nil, options={}, &block)
-
if template_class = self[file]
-
template_class.new(file, line, options, &block)
-
else
-
fail "No template engine registered for #{File.basename(file)}"
-
end
-
end
-
-
# Lookup a template class for the given filename or file
-
# extension. Return nil when no implementation is found.
-
1
def self.[](file)
-
pattern = file.to_s.downcase
-
until pattern.empty? || registered?(pattern)
-
pattern = File.basename(pattern)
-
pattern.sub!(/^[^.]*\.?/, '')
-
end
-
-
# Try to find a preferred engine.
-
preferred_klass = @preferred_mappings[pattern]
-
return preferred_klass if preferred_klass
-
-
# Fall back to the general list of mappings.
-
klasses = @template_mappings[pattern]
-
-
# Try to find an engine which is already loaded.
-
template = klasses.detect do |klass|
-
if klass.respond_to?(:engine_initialized?)
-
klass.engine_initialized?
-
end
-
end
-
-
return template if template
-
-
# Try each of the classes until one succeeds. If all of them fails,
-
# we'll raise the error of the first class.
-
first_failure = nil
-
-
klasses.each do |klass|
-
begin
-
klass.new { '' }
-
rescue Exception => ex
-
first_failure ||= ex
-
next
-
else
-
return klass
-
end
-
end
-
-
raise first_failure if first_failure
-
end
-
-
# Deprecated module.
-
1
module CompileSite
-
end
-
-
# Extremely simple template cache implementation. Calling applications
-
# create a Tilt::Cache instance and use #fetch with any set of hashable
-
# arguments (such as those to Tilt.new):
-
# cache = Tilt::Cache.new
-
# cache.fetch(path, line, options) { Tilt.new(path, line, options) }
-
#
-
# Subsequent invocations return the already loaded template object.
-
1
class Cache
-
1
def initialize
-
@cache = {}
-
end
-
-
1
def fetch(*key)
-
@cache[key] ||= yield
-
end
-
-
1
def clear
-
@cache = {}
-
end
-
end
-
-
-
# Template Implementations ================================================
-
-
1
require 'tilt/string'
-
1
register StringTemplate, 'str'
-
-
1
require 'tilt/erb'
-
1
register ERBTemplate, 'erb', 'rhtml'
-
1
register ErubisTemplate, 'erb', 'rhtml', 'erubis'
-
-
1
require 'tilt/haml'
-
1
register HamlTemplate, 'haml'
-
-
1
require 'tilt/css'
-
1
register SassTemplate, 'sass'
-
1
register ScssTemplate, 'scss'
-
1
register LessTemplate, 'less'
-
-
1
require 'tilt/coffee'
-
1
register CoffeeScriptTemplate, 'coffee'
-
-
1
require 'tilt/nokogiri'
-
1
register NokogiriTemplate, 'nokogiri'
-
-
1
require 'tilt/builder'
-
1
register BuilderTemplate, 'builder'
-
-
1
require 'tilt/markaby'
-
1
register MarkabyTemplate, 'mab'
-
-
1
require 'tilt/liquid'
-
1
register LiquidTemplate, 'liquid'
-
-
1
require 'tilt/radius'
-
1
register RadiusTemplate, 'radius'
-
-
1
require 'tilt/markdown'
-
1
register MarukuTemplate, 'markdown', 'mkd', 'md'
-
1
register KramdownTemplate, 'markdown', 'mkd', 'md'
-
1
register BlueClothTemplate, 'markdown', 'mkd', 'md'
-
1
register RDiscountTemplate, 'markdown', 'mkd', 'md'
-
1
register RedcarpetTemplate, 'markdown', 'mkd', 'md'
-
-
1
require 'tilt/textile'
-
1
register RedClothTemplate, 'textile'
-
-
1
require 'tilt/rdoc'
-
1
register RDocTemplate, 'rdoc'
-
-
1
require 'tilt/wiki'
-
1
register CreoleTemplate, 'wiki', 'creole'
-
1
register WikiClothTemplate, 'wiki', 'mediawiki', 'mw'
-
-
1
require 'tilt/yajl'
-
1
register YajlTemplate, 'yajl'
-
end
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# Builder template implementation. See:
-
# http://builder.rubyforge.org/
-
1
class BuilderTemplate < Template
-
1
self.default_mime_type = 'text/xml'
-
-
1
def self.engine_initialized?
-
defined? ::Builder
-
end
-
-
1
def initialize_engine
-
require_template_library 'builder'
-
end
-
-
1
def prepare; end
-
-
1
def evaluate(scope, locals, &block)
-
return super(scope, locals, &block) if data.respond_to?(:to_str)
-
xml = ::Builder::XmlMarkup.new(:indent => 2)
-
data.call(xml)
-
xml.target!
-
end
-
-
1
def precompiled_preamble(locals)
-
return super if locals.include? :xml
-
"xml = ::Builder::XmlMarkup.new(:indent => 2)\n#{super}"
-
end
-
-
1
def precompiled_postamble(locals)
-
"xml.target!"
-
end
-
-
1
def precompiled_template(locals)
-
data.to_str
-
end
-
end
-
end
-
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# CoffeeScript template implementation. See:
-
# http://coffeescript.org/
-
#
-
# CoffeeScript templates do not support object scopes, locals, or yield.
-
1
class CoffeeScriptTemplate < Template
-
1
self.default_mime_type = 'application/javascript'
-
-
1
@@default_bare = false
-
-
1
def self.default_bare
-
@@default_bare
-
end
-
-
1
def self.default_bare=(value)
-
@@default_bare = value
-
end
-
-
# DEPRECATED
-
1
def self.default_no_wrap
-
@@default_bare
-
end
-
-
# DEPRECATED
-
1
def self.default_no_wrap=(value)
-
@@default_bare = value
-
end
-
-
1
def self.engine_initialized?
-
defined? ::CoffeeScript
-
end
-
-
1
def initialize_engine
-
require_template_library 'coffee_script'
-
end
-
-
1
def prepare
-
if !options.key?(:bare) and !options.key?(:no_wrap)
-
options[:bare] = self.class.default_bare
-
end
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= CoffeeScript.compile(data, options)
-
end
-
end
-
end
-
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# Sass template implementation. See:
-
# http://haml.hamptoncatlin.com/
-
#
-
# Sass templates do not support object scopes, locals, or yield.
-
1
class SassTemplate < Template
-
1
self.default_mime_type = 'text/css'
-
-
1
def self.engine_initialized?
-
defined? ::Sass::Engine
-
end
-
-
1
def initialize_engine
-
require_template_library 'sass'
-
end
-
-
1
def prepare
-
@engine = ::Sass::Engine.new(data, sass_options)
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= @engine.render
-
end
-
-
1
private
-
1
def sass_options
-
options.merge(:filename => eval_file, :line => line, :syntax => :sass)
-
end
-
end
-
-
# Sass's new .scss type template implementation.
-
1
class ScssTemplate < SassTemplate
-
1
self.default_mime_type = 'text/css'
-
-
1
private
-
1
def sass_options
-
options.merge(:filename => eval_file, :line => line, :syntax => :scss)
-
end
-
end
-
-
# Lessscss template implementation. See:
-
# http://lesscss.org/
-
#
-
# Less templates do not support object scopes, locals, or yield.
-
1
class LessTemplate < Template
-
1
self.default_mime_type = 'text/css'
-
-
1
def self.engine_initialized?
-
defined? ::Less
-
end
-
-
1
def initialize_engine
-
require_template_library 'less'
-
end
-
-
1
def prepare
-
if ::Less.const_defined? :Engine
-
@engine = ::Less::Engine.new(data)
-
else
-
parser = ::Less::Parser.new(:filename => eval_file, :line => line)
-
@engine = parser.parse(data)
-
end
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= @engine.to_css
-
end
-
end
-
end
-
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# ERB template implementation. See:
-
# http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html
-
1
class ERBTemplate < Template
-
1
@@default_output_variable = '_erbout'
-
-
1
def self.default_output_variable
-
@@default_output_variable
-
end
-
-
1
def self.default_output_variable=(name)
-
@@default_output_variable = name
-
end
-
-
1
def self.engine_initialized?
-
defined? ::ERB
-
end
-
-
1
def initialize_engine
-
require_template_library 'erb'
-
end
-
-
1
def prepare
-
@outvar = options[:outvar] || self.class.default_output_variable
-
options[:trim] = '<>' if options[:trim].nil? || options[:trim] == true
-
@engine = ::ERB.new(data, options[:safe], options[:trim], @outvar)
-
end
-
-
1
def precompiled_template(locals)
-
source = @engine.src
-
source
-
end
-
-
1
def precompiled_preamble(locals)
-
<<-RUBY
-
begin
-
__original_outvar = #{@outvar} if defined?(#{@outvar})
-
#{super}
-
RUBY
-
end
-
-
1
def precompiled_postamble(locals)
-
<<-RUBY
-
#{super}
-
ensure
-
#{@outvar} = __original_outvar
-
end
-
RUBY
-
end
-
-
# ERB generates a line to specify the character coding of the generated
-
# source in 1.9. Account for this in the line offset.
-
1
if RUBY_VERSION >= '1.9.0'
-
1
def precompiled(locals)
-
source, offset = super
-
[source, offset + 1]
-
end
-
end
-
end
-
-
# Erubis template implementation. See:
-
# http://www.kuwata-lab.com/erubis/
-
#
-
# ErubisTemplate supports the following additional options, which are not
-
# passed down to the Erubis engine:
-
#
-
# :engine_class allows you to specify a custom engine class to use
-
# instead of the default (which is ::Erubis::Eruby).
-
#
-
# :escape_html when true, ::Erubis::EscapedEruby will be used as
-
# the engine class instead of the default. All content
-
# within <%= %> blocks will be automatically html escaped.
-
1
class ErubisTemplate < ERBTemplate
-
1
def self.engine_initialized?
-
defined? ::Erubis
-
end
-
-
1
def initialize_engine
-
require_template_library 'erubis'
-
end
-
-
1
def prepare
-
@outvar = options.delete(:outvar) || self.class.default_output_variable
-
@options.merge!(:preamble => false, :postamble => false, :bufvar => @outvar)
-
engine_class = options.delete(:engine_class)
-
engine_class = ::Erubis::EscapedEruby if options.delete(:escape_html)
-
@engine = (engine_class || ::Erubis::Eruby).new(data, options)
-
end
-
-
1
def precompiled_preamble(locals)
-
[super, "#{@outvar} = _buf = ''"].join("\n")
-
end
-
-
1
def precompiled_postamble(locals)
-
[@outvar, super].join("\n")
-
end
-
-
# Erubis doesn't have ERB's line-off-by-one under 1.9 problem.
-
# Override and adjust back.
-
1
if RUBY_VERSION >= '1.9.0'
-
1
def precompiled(locals)
-
source, offset = super
-
[source, offset - 1]
-
end
-
end
-
end
-
end
-
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# Haml template implementation. See:
-
# http://haml.hamptoncatlin.com/
-
1
class HamlTemplate < Template
-
1
self.default_mime_type = 'text/html'
-
-
1
def self.engine_initialized?
-
defined? ::Haml::Engine
-
end
-
-
1
def initialize_engine
-
require_template_library 'haml'
-
end
-
-
1
def prepare
-
options = @options.merge(:filename => eval_file, :line => line)
-
@engine = ::Haml::Engine.new(data, options)
-
end
-
-
1
def evaluate(scope, locals, &block)
-
if @engine.respond_to?(:precompiled_method_return_value, true)
-
super
-
else
-
@engine.render(scope, locals, &block)
-
end
-
end
-
-
# Precompiled Haml source. Taken from the precompiled_with_ambles
-
# method in Haml::Precompiler:
-
# http://github.com/nex3/haml/blob/master/lib/haml/precompiler.rb#L111-126
-
1
def precompiled_template(locals)
-
@engine.precompiled
-
end
-
-
1
def precompiled_preamble(locals)
-
local_assigns = super
-
@engine.instance_eval do
-
<<-RUBY
-
begin
-
extend Haml::Helpers
-
_hamlout = @haml_buffer = Haml::Buffer.new(@haml_buffer, #{options_for_buffer.inspect})
-
_erbout = _hamlout.buffer
-
__in_erb_template = true
-
_haml_locals = locals
-
#{local_assigns}
-
RUBY
-
end
-
end
-
-
1
def precompiled_postamble(locals)
-
@engine.instance_eval do
-
<<-RUBY
-
#{precompiled_method_return_value}
-
ensure
-
@haml_buffer = @haml_buffer.upper
-
end
-
RUBY
-
end
-
end
-
end
-
end
-
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# Liquid template implementation. See:
-
# http://liquid.rubyforge.org/
-
#
-
# Liquid is designed to be a *safe* template system and threfore
-
# does not provide direct access to execuatable scopes. In order to
-
# support a +scope+, the +scope+ must be able to represent itself
-
# as a hash by responding to #to_h. If the +scope+ does not respond
-
# to #to_h it will be ignored.
-
#
-
# LiquidTemplate does not support yield blocks.
-
#
-
# It's suggested that your program require 'liquid' at load
-
# time when using this template engine.
-
1
class LiquidTemplate < Template
-
1
def self.engine_initialized?
-
defined? ::Liquid::Template
-
end
-
-
1
def initialize_engine
-
require_template_library 'liquid'
-
end
-
-
1
def prepare
-
@engine = ::Liquid::Template.parse(data)
-
end
-
-
1
def evaluate(scope, locals, &block)
-
locals = locals.inject({}){ |h,(k,v)| h[k.to_s] = v ; h }
-
if scope.respond_to?(:to_h)
-
scope = scope.to_h.inject({}){ |h,(k,v)| h[k.to_s] = v ; h }
-
locals = scope.merge(locals)
-
end
-
locals['yield'] = block.nil? ? '' : yield
-
locals['content'] = locals['yield']
-
@engine.render(locals)
-
end
-
end
-
end
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# Markaby
-
# http://github.com/markaby/markaby
-
1
class MarkabyTemplate < Template
-
1
def self.builder_class
-
@builder_class ||= Class.new(Markaby::Builder) do
-
def __capture_markaby_tilt__(&block)
-
__run_markaby_tilt__ do
-
text capture(&block)
-
end
-
end
-
end
-
end
-
-
1
def self.engine_initialized?
-
defined? ::Markaby
-
end
-
-
1
def initialize_engine
-
require_template_library 'markaby'
-
end
-
-
1
def prepare
-
end
-
-
1
def evaluate(scope, locals, &block)
-
builder = self.class.builder_class.new({}, scope)
-
builder.locals = locals
-
-
if data.kind_of? Proc
-
(class << builder; self end).send(:define_method, :__run_markaby_tilt__, &data)
-
else
-
builder.instance_eval <<-CODE, __FILE__, __LINE__
-
def __run_markaby_tilt__
-
#{data}
-
end
-
CODE
-
end
-
-
if block
-
builder.__capture_markaby_tilt__(&block)
-
else
-
builder.__run_markaby_tilt__
-
end
-
-
builder.to_s
-
end
-
end
-
end
-
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# Discount Markdown implementation. See:
-
# http://github.com/rtomayko/rdiscount
-
#
-
# RDiscount is a simple text filter. It does not support +scope+ or
-
# +locals+. The +:smart+ and +:filter_html+ options may be set true
-
# to enable those flags on the underlying RDiscount object.
-
1
class RDiscountTemplate < Template
-
1
self.default_mime_type = 'text/html'
-
-
1
ALIAS = {
-
:escape_html => :filter_html,
-
:smartypants => :smart
-
}
-
-
1
FLAGS = [:smart, :filter_html, :smartypants, :escape_html]
-
-
1
def flags
-
FLAGS.select { |flag| options[flag] }.map { |flag| ALIAS[flag] || flag }
-
end
-
-
1
def self.engine_initialized?
-
defined? ::RDiscount
-
end
-
-
1
def initialize_engine
-
require_template_library 'rdiscount'
-
end
-
-
1
def prepare
-
@engine = RDiscount.new(data, *flags)
-
@output = nil
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= @engine.to_html
-
end
-
end
-
-
# Upskirt Markdown implementation. See:
-
# https://github.com/tanoku/redcarpet
-
#
-
# Supports both Redcarpet 1.x and 2.x
-
1
class RedcarpetTemplate < Template
-
1
def self.engine_initialized?
-
defined? ::Redcarpet
-
end
-
-
1
def initialize_engine
-
require_template_library 'redcarpet'
-
end
-
-
1
def prepare
-
klass = [Redcarpet1, Redcarpet2].detect { |e| e.engine_initialized? }
-
@engine = klass.new(file, line, options) { data }
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@engine.evaluate(scope, locals, &block)
-
end
-
-
# Compatibility mode for Redcarpet 1.x
-
1
class Redcarpet1 < RDiscountTemplate
-
1
self.default_mime_type = 'text/html'
-
-
1
def self.engine_initialized?
-
defined? ::RedcarpetCompat
-
end
-
-
1
def prepare
-
@engine = RedcarpetCompat.new(data, *flags)
-
@output = nil
-
end
-
end
-
-
# Future proof mode for Redcarpet 2.x (not yet released)
-
1
class Redcarpet2 < Template
-
1
self.default_mime_type = 'text/html'
-
-
1
def self.engine_initialized?
-
defined? ::Redcarpet::Render
-
end
-
-
1
def generate_renderer
-
renderer = options.delete(:renderer) || Redcarpet::Render::HTML
-
return renderer unless options.delete(:smartypants)
-
return renderer if renderer <= Redcarpet::Render::SmartyPants
-
-
if renderer == Redcarpet::Render::XHTML
-
Redcarpet::Render::SmartyHTML.new(:xhtml => true)
-
elsif renderer == Redcarpet::Render::HTML
-
Redcarpet::Render::SmartyHTML
-
elsif renderer.is_a? Class
-
Class.new(renderer) { include Redcarpet::Render::SmartyPants }
-
else
-
renderer.extend Redcarpet::Render::SmartyPants
-
end
-
end
-
-
1
def prepare
-
# try to support the same aliases
-
RDiscountTemplate::ALIAS.each do |opt, aka|
-
next if options.key? opt or not options.key? aka
-
options[opt] = options.delete(aka)
-
end
-
-
# only raise an exception if someone is trying to enable :escape_html
-
options.delete(:escape_html) unless options[:escape_html]
-
-
@engine = Redcarpet::Markdown.new(generate_renderer, options)
-
@output = nil
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= @engine.render(data)
-
end
-
end
-
end
-
-
# BlueCloth Markdown implementation. See:
-
# http://deveiate.org/projects/BlueCloth/
-
1
class BlueClothTemplate < Template
-
1
self.default_mime_type = 'text/html'
-
-
1
def self.engine_initialized?
-
defined? ::BlueCloth
-
end
-
-
1
def initialize_engine
-
require_template_library 'bluecloth'
-
end
-
-
1
def prepare
-
@engine = BlueCloth.new(data, options)
-
@output = nil
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= @engine.to_html
-
end
-
end
-
-
# Maruku markdown implementation. See:
-
# http://maruku.rubyforge.org/
-
1
class MarukuTemplate < Template
-
1
def self.engine_initialized?
-
defined? ::Maruku
-
end
-
-
1
def initialize_engine
-
require_template_library 'maruku'
-
end
-
-
1
def prepare
-
@engine = Maruku.new(data, options)
-
@output = nil
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= @engine.to_html
-
end
-
end
-
-
# Kramdown Markdown implementation. See:
-
# http://kramdown.rubyforge.org/
-
1
class KramdownTemplate < Template
-
1
DUMB_QUOTES = [39, 39, 34, 34]
-
-
1
def self.engine_initialized?
-
defined? ::Kramdown
-
end
-
-
1
def initialize_engine
-
require_template_library 'kramdown'
-
end
-
-
1
def prepare
-
options[:smart_quotes] = DUMB_QUOTES unless options[:smartypants]
-
@engine = Kramdown::Document.new(data, options)
-
@output = nil
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= @engine.to_html
-
end
-
end
-
end
-
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# Nokogiri template implementation. See:
-
# http://nokogiri.org/
-
1
class NokogiriTemplate < Template
-
1
self.default_mime_type = 'text/xml'
-
-
1
def self.engine_initialized?
-
defined? ::Nokogiri
-
end
-
-
1
def initialize_engine
-
require_template_library 'nokogiri'
-
end
-
-
1
def prepare; end
-
-
1
def evaluate(scope, locals, &block)
-
block &&= proc { yield.gsub(/^<\?xml version=\"1\.0\"\?>\n?/, "") }
-
-
if data.respond_to?(:to_str)
-
super(scope, locals, &block)
-
else
-
::Nokogiri::XML::Builder.new.tap(&data).to_xml
-
end
-
end
-
-
1
def precompiled_preamble(locals)
-
return super if locals.include? :xml
-
"xml = ::Nokogiri::XML::Builder.new { |xml| }\n#{super}"
-
end
-
-
1
def precompiled_postamble(locals)
-
"xml.to_xml"
-
end
-
-
1
def precompiled_template(locals)
-
data.to_str
-
end
-
end
-
end
-
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# Radius Template
-
# http://github.com/jlong/radius/
-
1
class RadiusTemplate < Template
-
1
def self.engine_initialized?
-
defined? ::Radius
-
end
-
-
1
def self.context_class
-
@context_class ||= Class.new(Radius::Context) do
-
attr_accessor :tilt_scope
-
-
def tag_missing(name, attributes)
-
tilt_scope.__send__(name)
-
end
-
-
def dup
-
i = super
-
i.tilt_scope = tilt_scope
-
i
-
end
-
end
-
end
-
-
1
def initialize_engine
-
require_template_library 'radius'
-
end
-
-
1
def prepare
-
end
-
-
1
def evaluate(scope, locals, &block)
-
context = self.class.context_class.new
-
context.tilt_scope = scope
-
context.define_tag("yield") do
-
block.call
-
end
-
locals.each do |tag, value|
-
context.define_tag(tag) do
-
value
-
end
-
end
-
-
options = {:tag_prefix => 'r'}.merge(@options)
-
parser = Radius::Parser.new(context, options)
-
parser.parse(data)
-
end
-
end
-
end
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# RDoc template. See:
-
# http://rdoc.rubyforge.org/
-
#
-
# It's suggested that your program require 'rdoc/markup' and
-
# 'rdoc/markup/to_html' at load time when using this template
-
# engine.
-
1
class RDocTemplate < Template
-
1
self.default_mime_type = 'text/html'
-
-
1
def self.engine_initialized?
-
defined? ::RDoc::Markup
-
end
-
-
1
def initialize_engine
-
require_template_library 'rdoc/markup'
-
require_template_library 'rdoc/markup/to_html'
-
end
-
-
1
def prepare
-
markup = RDoc::Markup::ToHtml.new
-
@engine = markup.convert(data)
-
@output = nil
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= @engine.to_s
-
end
-
end
-
end
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# The template source is evaluated as a Ruby string. The #{} interpolation
-
# syntax can be used to generated dynamic output.
-
1
class StringTemplate < Template
-
1
def prepare
-
hash = "TILT#{data.hash.abs}"
-
@code = "<<#{hash}.chomp\n#{data}\n#{hash}"
-
end
-
-
1
def precompiled_template(locals)
-
@code
-
end
-
-
1
def precompiled(locals)
-
source, offset = super
-
[source, offset + 1]
-
end
-
end
-
end
-
1
module Tilt
-
1
TOPOBJECT = defined?(BasicObject) ? BasicObject : Object
-
-
# Base class for template implementations. Subclasses must implement
-
# the #prepare method and one of the #evaluate or #precompiled_template
-
# methods.
-
1
class Template
-
# Template source; loaded from a file or given directly.
-
1
attr_reader :data
-
-
# The name of the file where the template data was loaded from.
-
1
attr_reader :file
-
-
# The line number in #file where template data was loaded from.
-
1
attr_reader :line
-
-
# A Hash of template engine specific options. This is passed directly
-
# to the underlying engine and is not used by the generic template
-
# interface.
-
1
attr_reader :options
-
-
# Used to determine if this class's initialize_engine method has
-
# been called yet.
-
1
@engine_initialized = false
-
1
class << self
-
1
attr_accessor :engine_initialized
-
1
alias engine_initialized? engine_initialized
-
-
1
attr_accessor :default_mime_type
-
end
-
-
# Create a new template with the file, line, and options specified. By
-
# default, template data is read from the file. When a block is given,
-
# it should read template data and return as a String. When file is nil,
-
# a block is required.
-
#
-
# All arguments are optional.
-
1
def initialize(file=nil, line=1, options={}, &block)
-
@file, @line, @options = nil, 1, {}
-
-
[options, line, file].compact.each do |arg|
-
case
-
when arg.respond_to?(:to_str) ; @file = arg.to_str
-
when arg.respond_to?(:to_int) ; @line = arg.to_int
-
when arg.respond_to?(:to_hash) ; @options = arg.to_hash.dup
-
else raise TypeError
-
end
-
end
-
-
raise ArgumentError, "file or block required" if (@file || block).nil?
-
-
# call the initialize_engine method if this is the very first time
-
# an instance of this class has been created.
-
if !self.class.engine_initialized?
-
initialize_engine
-
self.class.engine_initialized = true
-
end
-
-
# used to hold compiled template methods
-
@compiled_method = {}
-
-
# used on 1.9 to set the encoding if it is not set elsewhere (like a magic comment)
-
# currently only used if template compiles to ruby
-
@default_encoding = @options.delete :default_encoding
-
-
# load template data and prepare (uses binread to avoid encoding issues)
-
@reader = block || lambda { |t| File.respond_to?(:binread) ? File.binread(@file) : File.read(@file) }
-
@data = @reader.call(self)
-
prepare
-
end
-
-
# Render the template in the given scope with the locals specified. If a
-
# block is given, it is typically available within the template via
-
# +yield+.
-
1
def render(scope=Object.new, locals={}, &block)
-
evaluate scope, locals || {}, &block
-
end
-
-
# The basename of the template file.
-
1
def basename(suffix='')
-
File.basename(file, suffix) if file
-
end
-
-
# The template file's basename with all extensions chomped off.
-
1
def name
-
basename.split('.', 2).first if basename
-
end
-
-
# The filename used in backtraces to describe the template.
-
1
def eval_file
-
file || '(__TEMPLATE__)'
-
end
-
-
1
protected
-
# Called once and only once for each template subclass the first time
-
# the template class is initialized. This should be used to require the
-
# underlying template library and perform any initial setup.
-
1
def initialize_engine
-
end
-
-
# Like Kernel::require but issues a warning urging a manual require when
-
# running under a threaded environment.
-
1
def require_template_library(name)
-
if Thread.list.size > 1
-
warn "WARN: tilt autoloading '#{name}' in a non thread-safe way; " +
-
"explicit require '#{name}' suggested."
-
end
-
require name
-
end
-
-
# Do whatever preparation is necessary to setup the underlying template
-
# engine. Called immediately after template data is loaded. Instance
-
# variables set in this method are available when #evaluate is called.
-
#
-
# Subclasses must provide an implementation of this method.
-
1
def prepare
-
if respond_to?(:compile!)
-
# backward compat with tilt < 0.6; just in case
-
warn 'Tilt::Template#compile! is deprecated; implement #prepare instead.'
-
compile!
-
else
-
raise NotImplementedError
-
end
-
end
-
-
1
def evaluate(scope, locals, &block)
-
cached_evaluate(scope, locals, &block)
-
end
-
-
# Process the template and return the result. The first time this
-
# method is called, the template source is evaluated with instance_eval.
-
# On the sequential method calls it will compile the template to an
-
# unbound method which will lead to better performance. In any case,
-
# template executation is guaranteed to be performed in the scope object
-
# with the locals specified and with support for yielding to the block.
-
1
def cached_evaluate(scope, locals, &block)
-
# Redefine itself to use method compilation the next time:
-
def self.cached_evaluate(scope, locals, &block)
-
method = compiled_method(locals.keys)
-
method.bind(scope).call(locals, &block)
-
end
-
-
# Use instance_eval the first time:
-
evaluate_source(scope, locals, &block)
-
end
-
-
# Generates all template source by combining the preamble, template, and
-
# postamble and returns a two-tuple of the form: [source, offset], where
-
# source is the string containing (Ruby) source code for the template and
-
# offset is the integer line offset where line reporting should begin.
-
#
-
# Template subclasses may override this method when they need complete
-
# control over source generation or want to adjust the default line
-
# offset. In most cases, overriding the #precompiled_template method is
-
# easier and more appropriate.
-
1
def precompiled(locals)
-
preamble = precompiled_preamble(locals)
-
template = precompiled_template(locals)
-
magic_comment = extract_magic_comment(template)
-
if magic_comment
-
# Magic comment e.g. "# coding: utf-8" has to be in the first line.
-
# So we copy the magic comment to the first line.
-
preamble = magic_comment + "\n" + preamble
-
end
-
parts = [
-
preamble,
-
template,
-
precompiled_postamble(locals)
-
]
-
[parts.join("\n"), preamble.count("\n") + 1]
-
end
-
-
# A string containing the (Ruby) source code for the template. The
-
# default Template#evaluate implementation requires either this method
-
# or the #precompiled method be overridden. When defined, the base
-
# Template guarantees correct file/line handling, locals support, custom
-
# scopes, and support for template compilation when the scope object
-
# allows it.
-
1
def precompiled_template(locals)
-
raise NotImplementedError
-
end
-
-
# Generates preamble code for initializing template state, and performing
-
# locals assignment. The default implementation performs locals
-
# assignment only. Lines included in the preamble are subtracted from the
-
# source line offset, so adding code to the preamble does not effect line
-
# reporting in Kernel::caller and backtraces.
-
1
def precompiled_preamble(locals)
-
locals.map { |k,v| "#{k} = locals[#{k.inspect}]" }.join("\n")
-
end
-
-
# Generates postamble code for the precompiled template source. The
-
# string returned from this method is appended to the precompiled
-
# template source.
-
1
def precompiled_postamble(locals)
-
''
-
end
-
-
# The compiled method for the locals keys provided.
-
1
def compiled_method(locals_keys)
-
@compiled_method[locals_keys] ||=
-
compile_template_method(locals_keys)
-
end
-
-
1
private
-
# Evaluate the template source in the context of the scope object.
-
1
def evaluate_source(scope, locals, &block)
-
source, offset = precompiled(locals)
-
scope.instance_eval(source, eval_file, line - offset)
-
end
-
-
# JRuby doesn't allow Object#instance_eval to yield to the block it's
-
# closed over. This is by design and (ostensibly) something that will
-
# change in MRI, though no current MRI version tested (1.8.6 - 1.9.2)
-
# exhibits the behavior. More info here:
-
#
-
# http://jira.codehaus.org/browse/JRUBY-2599
-
#
-
# We redefine evaluate_source to work around this issues.
-
1
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
-
undef evaluate_source
-
def evaluate_source(scope, locals, &block)
-
source, offset = precompiled(locals)
-
file, lineno = eval_file, (line - offset)
-
scope.instance_eval { Kernel::eval(source, binding, file, lineno) }
-
end
-
end
-
-
1
def compile_template_method(locals)
-
source, offset = precompiled(locals)
-
offset += 5
-
method_name = "__tilt_#{Thread.current.object_id.abs}"
-
Object.class_eval <<-RUBY, eval_file, line - offset
-
#{extract_magic_comment source}
-
TOPOBJECT.class_eval do
-
def #{method_name}(locals)
-
Thread.current[:tilt_vars] = [self, locals]
-
class << self
-
this, locals = Thread.current[:tilt_vars]
-
this.instance_eval do
-
#{source}
-
end
-
end
-
end
-
end
-
RUBY
-
unbind_compiled_method(method_name)
-
end
-
-
1
def unbind_compiled_method(method_name)
-
method = TOPOBJECT.instance_method(method_name)
-
TOPOBJECT.class_eval { remove_method(method_name) }
-
method
-
end
-
-
1
def extract_magic_comment(script)
-
comment = script.slice(/\A[ \t]*\#.*coding\s*[=:]\s*([[:alnum:]\-_]+).*$/)
-
return comment if comment and not %w[ascii-8bit binary].include?($1.downcase)
-
"# coding: #{@default_encoding}" if @default_encoding
-
end
-
-
# Special case Ruby 1.9.1's broken yield.
-
#
-
# http://github.com/rtomayko/tilt/commit/20c01a5
-
# http://redmine.ruby-lang.org/issues/show/3601
-
#
-
# Remove when 1.9.2 dominates 1.9.1 installs in the wild.
-
1
if RUBY_VERSION =~ /^1.9.1/
-
undef compile_template_method
-
def compile_template_method(locals)
-
source, offset = precompiled(locals)
-
offset += 1
-
method_name = "__tilt_#{Thread.current.object_id}"
-
Object.class_eval <<-RUBY, eval_file, line - offset
-
TOPOBJECT.class_eval do
-
def #{method_name}(locals)
-
#{source}
-
end
-
end
-
RUBY
-
unbind_compiled_method(method_name)
-
end
-
end
-
end
-
end
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# RedCloth implementation. See:
-
# http://redcloth.org/
-
1
class RedClothTemplate < Template
-
1
def self.engine_initialized?
-
defined? ::RedCloth
-
end
-
-
1
def initialize_engine
-
require_template_library 'redcloth'
-
end
-
-
1
def prepare
-
@engine = RedCloth.new(data)
-
@output = nil
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= @engine.to_html
-
end
-
end
-
end
-
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# Creole implementation. See:
-
# http://www.wikicreole.org/
-
1
class CreoleTemplate < Template
-
1
def self.engine_initialized?
-
defined? ::Creole
-
end
-
-
1
def initialize_engine
-
require_template_library 'creole'
-
end
-
-
1
def prepare
-
opts = {}
-
[:allowed_schemes, :extensions, :no_escape].each do |k|
-
opts[k] = options[k] if options[k]
-
end
-
@engine = Creole::Parser.new(data, opts)
-
@output = nil
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= @engine.to_html
-
end
-
end
-
-
# WikiCloth implementation. See:
-
# http://redcloth.org/
-
1
class WikiClothTemplate < Template
-
1
def self.engine_initialized?
-
defined? ::WikiCloth::Parser
-
end
-
-
1
def initialize_engine
-
require_template_library 'wikicloth'
-
end
-
-
1
def prepare
-
@parser = options.delete(:parser) || WikiCloth::Parser
-
@engine = @parser.new options.merge(:data => data)
-
@output = nil
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= @engine.to_html
-
end
-
end
-
end
-
1
require 'tilt/template'
-
-
1
module Tilt
-
-
# Yajl Template implementation
-
#
-
# Yajl is a fast JSON parsing and encoding library for Ruby
-
# See https://github.com/brianmario/yajl-ruby
-
#
-
# The template source is evaluated as a Ruby string,
-
# and the result is converted #to_json.
-
#
-
# == Example
-
#
-
# # This is a template example.
-
# # The template can contain any Ruby statement.
-
# tpl <<-EOS
-
# @counter = 0
-
#
-
# # The json variable represents the buffer
-
# # and holds the data to be serialized into json.
-
# # It defaults to an empty hash, but you can override it at any time.
-
# json = {
-
# :"user#{@counter += 1}" => { :name => "Joshua Peek", :id => @counter },
-
# :"user#{@counter += 1}" => { :name => "Ryan Tomayko", :id => @counter },
-
# :"user#{@counter += 1}" => { :name => "Simone Carletti", :id => @counter },
-
# }
-
#
-
# # Since the json variable is a Hash,
-
# # you can use conditional statements or any other Ruby statement
-
# # to populate it.
-
# json[:"user#{@counter += 1}"] = { :name => "Unknown" } if 1 == 2
-
#
-
# # The last line doesn't affect the returned value.
-
# nil
-
# EOS
-
#
-
# template = Tilt::YajlTemplate.new { tpl }
-
# template.render(self)
-
#
-
1
class YajlTemplate < Template
-
-
1
self.default_mime_type = 'application/json'
-
-
1
def self.engine_initialized?
-
defined? ::Yajl
-
end
-
-
1
def initialize_engine
-
require_template_library 'yajl'
-
end
-
-
1
def prepare
-
end
-
-
1
def evaluate(scope, locals, &block)
-
decorate super(scope, locals, &block)
-
end
-
-
1
def precompiled_preamble(locals)
-
return super if locals.include? :json
-
"json = {}\n#{super}"
-
end
-
-
1
def precompiled_postamble(locals)
-
"Yajl::Encoder.new.encode(json)"
-
end
-
-
1
def precompiled_template(locals)
-
data.to_str
-
end
-
-
-
# Decorates the +json+ input according to given +options+.
-
#
-
# json - The json String to decorate.
-
# options - The option Hash to customize the behavior.
-
#
-
# Returns the decorated String.
-
1
def decorate(json)
-
callback, variable = options[:callback], options[:variable]
-
if callback && variable
-
"var #{variable} = #{json}; #{callback}(#{variable});"
-
elsif variable
-
"var #{variable} = #{json};"
-
elsif callback
-
"#{callback}(#{json});"
-
else
-
json
-
end
-
end
-
end
-
-
end
-
1
module Turn
-
end
-
-
1
require 'fileutils'
-
-
1
require 'turn/version'
-
1
require 'turn/autoload'
-
1
require 'turn/configuration'
-
1
require 'turn/colorize'
-
1
require 'turn/components'
-
1
require 'turn/controller'
-
1
require 'turn/command'
-
1
require 'turn/minitest'
-
-
# This is a "dirty trick" to load `test/unit` or `minitest/unit` if sought.
-
# Eventually a way to handle this more robustly (without autoreload)
-
# should be worked out. But how?
-
1
autoload "Test", 'test/unit'
-
1
autoload "MiniTest", 'minitest/unit'
-
-
1
require 'turn/configuration' # why is this needed here?
-
-
1
begin
-
1
require 'ansi/code'
-
rescue LoadError
-
begin
-
require 'rubygems'
-
require 'ansi/code'
-
rescue LoadError
-
end
-
end
-
-
1
module Turn
-
-
# Provides a uniform interface for colorizing Turn output.
-
#
-
1
module Colorize
-
-
1
def self.included(base)
-
base.module_eval do
-
const_set :PASS, Colorize.pass('PASS')
-
const_set :FAIL, Colorize.fail('FAIL')
-
const_set :ERROR, Colorize.error('ERROR')
-
const_set :SKIP, Colorize.skip('SKIP')
-
end
-
end
-
-
1
COLORLESS_TERMINALS = ['dumb']
-
-
# Colorize output or not?
-
1
def self.colorize?
-
return @colorize unless @colorize.nil?
-
@colorize ||= (
-
ansi = Turn.config.ansi?
-
ansi.nil? ? color_supported? : ansi
-
)
-
end
-
-
# Does the system support color?
-
1
def self.color_supported?
-
return false unless defined?(::ANSI::Code)
-
return false unless $stdout.tty?
-
return true if ENV.has_key?('TERM') && !COLORLESS_TERMINALS.include?(ENV['TERM'])
-
return true if ::RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ && ENV.has_key?('ANSICON')
-
return false
-
end
-
-
1
def self.red(string)
-
colorize? ? ::ANSI::Code.red{ string } : string
-
end
-
-
1
def self.green(string)
-
colorize? ? ::ANSI::Code.green{ string } : string
-
end
-
-
1
def self.blue(string)
-
colorize? ? ::ANSI::Code.blue{ string } : string
-
end
-
-
1
def self.magenta(string)
-
colorize? ? ::ANSI::Code.magenta{ string } : string
-
end
-
-
1
def self.bold(string)
-
colorize? ? ::ANSI::Code.bold{ string } : string
-
end
-
-
1
def self.mark(string)
-
colorize? ? ::ANSI::Code.yellow{ string } : string
-
end
-
-
1
def self.pass(string)
-
colorize? ? ::ANSI::Code.green{ string } : string
-
end
-
-
1
def self.fail(string)
-
colorize? ? ::ANSI::Code.red{ string } : string
-
end
-
-
1
def self.error(string)
-
#colorize? ? ::ANSI::Code.white_on_red{ string } : string
-
colorize? ? ::ANSI::Code.yellow{ string } : string
-
end
-
-
1
def self.skip(string)
-
colorize? ? ::ANSI::Code.cyan{ string } : string
-
end
-
-
1
def colorize?
-
Colorize.colorize?
-
end
-
-
end
-
-
end
-
-
1
require 'optparse'
-
-
1
module Turn
-
-
# Turn - Pretty Unit Test Runner for Ruby
-
#
-
# SYNOPSIS
-
# turn [OPTIONS] [RUN MODE] [OUTPUT MODE] [TEST GLOBS ...]
-
#
-
# GENERAL OPTIONS
-
# -I, --loadpath=PATHS add paths to $LOAD_PATH
-
# -r, --require=LIBS require libraries
-
# -n, --name=PATTERN only run tests that match PATTERN
-
# -c, --case=PATTERN only run test cases that match PATTERN
-
# -b, --backtrace, --trace INT Limit the number of lines of backtrace.
-
# --natural Show natualized test names.
-
# --[no-]ansi Force use of ANSI codes on or off.
-
# --log log results to a file
-
# --live do not use local load path
-
#
-
# RUN MODES
-
# --normal run all tests in a single process [default]
-
# --solo run each test in a separate process
-
# --cross run each pair of test files in a separate process
-
#
-
# OUTPUT MODES
-
# -O, --outline turn's original case/test outline mode [default]
-
# -P, --progress indicates progress with progress bar
-
# -D, --dotted test-unit's traditonal dot-progress mode
-
# -R, -T, --pretty new pretty output mode
-
# -C, --cue cue for action on each failure/error
-
# -M, --marshal dump output as YAML (normal run mode only)
-
#
-
# COMMAND OPTIONS
-
# --debug turn debug mode on
-
# --version display version
-
# -h, --help display this help information
-
#
-
1
class Command
-
-
# Shortcut for new.main(*argv)
-
1
def self.main(*argv)
-
new.main(*argv)
-
end
-
-
# Log output.
-
1
attr :log
-
-
# Do not use local loadpath.
-
1
attr :live
-
-
# Only run tests matching this pattern.
-
1
attr :pattern
-
-
# Only run testcases matching this pattern.
-
1
attr :matchcase
-
-
# List of paths to add to $LOAD_PATH
-
1
attr :loadpath
-
-
# Libraries to require before running tests.
-
1
attr :requires
-
-
# Framework to use, :minitest or :testunit.
-
#attr :framework
-
-
# Run mode.
-
1
attr :runmode
-
-
# Output mode.
-
1
attr :outmode
-
-
# Enable full backtrace
-
1
attr :trace
-
-
# Use natural test case names.
-
1
attr :natural
-
-
# Show extra information.
-
1
attr :verbose
-
-
# Show extra information.
-
1
attr :mark
-
-
# Force ANSI use on or off.
-
1
attr :ansi
-
-
#
-
1
def initialize
-
@live = nil
-
@log = nil
-
@pattern = nil
-
@matchcase = nil
-
@loadpath = []
-
@requires = []
-
@runmode = nil
-
@outmode = nil
-
@trace = nil
-
@natural = false
-
@verbose = false
-
@mark = nil
-
@ansi = nil
-
end
-
-
#
-
1
def option_parser
-
OptionParser.new do |opts|
-
-
opts.banner = "Turn - Pretty Unit Test Runner for Ruby"
-
-
opts.separator " "
-
opts.separator "SYNOPSIS"
-
opts.separator " turn [OPTIONS] [RUN MODE] [OUTPUT MODE] [TEST GLOBS ...]"
-
-
opts.separator " "
-
opts.separator "GENERAL OPTIONS"
-
-
opts.on('-I', '--loadpath=PATHS', "add paths to $LOAD_PATH") do |path|
-
@loadpath.concat(path.split(':'))
-
end
-
-
opts.on('-r', '--require=LIBS', "require libraries") do |lib|
-
@requires.concat(lib.split(':'))
-
end
-
-
opts.on('-n', '--name=PATTERN', "only run tests that match PATTERN") do |pattern|
-
if pattern =~ /\/(.*)\//
-
@pattern = Regexp.new($1)
-
else
-
@pattern = Regexp.new(pattern, Regexp::IGNORECASE)
-
end
-
end
-
-
opts.on('-c', '--case=PATTERN', "only run test cases that match PATTERN") do |pattern|
-
if pattern =~ /\/(.*)\//
-
@matchcase = Regexp.new($1)
-
else
-
@matchcase = Regexp.new(pattern, Regexp::IGNORECASE)
-
end
-
end
-
-
opts.on('-m', '--mark=SECONDS', "Mark test if it exceeds runtime threshold.") do |int|
-
@mark = int.to_i
-
end
-
-
opts.on('-b', '--backtrace', '--trace INT', "Limit the number of lines of backtrace.") do |int|
-
@trace = int
-
end
-
-
opts.on('--natural', "Show natualized test names.") do |bool|
-
@natural = bool
-
end
-
-
opts.on('-v', '--verbose', "Show extra information.") do |bool|
-
@verbose = bool
-
end
-
-
opts.on('--[no-]ansi', "Force use of ANSI codes on or off.") do |bool|
-
@ansi = bool
-
end
-
-
# Turn does not support Test::Unit 2.0+
-
#opts.on('-u', '--testunit', "Force use of TestUnit framework") do
-
# @framework = :testunit
-
#end
-
-
opts.on('--log', "log results to a file") do #|path|
-
@log = true # TODO: support path/file
-
end
-
-
opts.on('--live', "do not use local load path") do
-
@live = true
-
end
-
-
opts.separator " "
-
opts.separator "RUN MODES"
-
-
opts.on('--normal', "run all tests in a single process [default]") do
-
@runmode = nil
-
end
-
-
opts.on('--solo', "run each test in a separate process") do
-
@runmode = :solo
-
end
-
-
opts.on('--cross', "run each pair of test files in a separate process") do
-
@runmode = :cross
-
end
-
-
#opts.on('--load', "") do
-
#end
-
-
opts.separator " "
-
opts.separator "OUTPUT MODES"
-
-
opts.on('--outline', '-O', "turn's original case/test outline mode [default]") do
-
@outmode = :outline
-
end
-
-
opts.on('--progress', '-P', "indicates progress with progress bar") do
-
@outmode = :progress
-
end
-
-
opts.on('--dotted', '-D', "test-unit's traditonal dot-progress mode") do
-
@outmode = :dotted
-
end
-
-
opts.on('--pretty', '-R', '-T', "new pretty output mode") do
-
@outmode = :pretty
-
end
-
-
opts.on('--cue', '-C', "cue for action on each failure/error") do
-
@outmode = :cue
-
end
-
-
opts.on('--marshal', '-M', "dump output as YAML (normal run mode only)") do
-
@runmode = :marshal
-
@outmode = :marshal
-
end
-
-
opts.separator " "
-
opts.separator "COMMAND OPTIONS"
-
-
opts.on('--debug', "turn debug mode on") do
-
$DEBUG = true
-
end
-
-
opts.on('--warn', "turn warnings on") do
-
$VERBOSE = true
-
end
-
-
opts.on_tail('--version', "display version") do
-
puts VERSION
-
exit
-
end
-
-
opts.on_tail('-h', '--help', "display this help information") do
-
puts opts
-
exit
-
end
-
end
-
end
-
-
# Run command.
-
1
def main(*argv)
-
option_parser.parse!(argv)
-
-
@loadpath = ['lib'] if loadpath.empty?
-
-
tests = ARGV.empty? ? nil : argv.dup
-
-
#config = Turn::Configuration.new do |c|
-
config = Turn.config do |c|
-
c.live = live
-
c.log = log
-
c.loadpath = loadpath
-
c.requires = requires
-
c.tests = tests
-
c.runmode = runmode
-
c.format = outmode
-
c.pattern = pattern
-
c.matchcase = matchcase
-
c.trace = trace
-
c.natural = natural
-
c.verbose = verbose
-
c.mark = mark
-
c.ansi = ansi unless ansi.nil?
-
end
-
-
controller = Turn::Controller.new(config)
-
-
result = controller.start
-
-
if result
-
exit (result.passed? ? 0 : -1)
-
else # no tests
-
exit -1
-
end
-
end
-
-
end
-
-
end
-
1
require 'turn/components/suite'
-
1
require 'turn/components/case'
-
1
require 'turn/components/method'
-
-
1
module Turn
-
-
#
-
1
class TestCase
-
1
include Enumerable
-
-
# Name of test case.
-
1
attr_accessor :name
-
-
# Test methods.
-
1
attr_accessor :tests
-
-
# Some runners marshal tests per file.
-
1
attr_accessor :files
-
-
#attr_accessor :count_passes
-
#attr_accessor :count_failures
-
#attr_accessor :count_errors
-
#attr_accessor :count_tests
-
-
# This can't be calculated, so it must be
-
# assigned by the runner.
-
1
attr_accessor :count_assertions
-
-
# Holds dump of test output (optional depending on runner).
-
1
attr_writer :message
-
-
# Command used to run test (optional depending on runner).
-
#attr_accessor :command
-
-
#
-
1
def initialize(name, *files)
-
@name = name
-
@files = (files.empty? ? [name] : files)
-
@tests = []
-
-
@message = nil
-
@count_assertions = 0
-
-
#@count_tests = 0
-
#@count_failures = 0
-
#@count_errors = 0
-
-
#@command = command
-
end
-
-
1
def new_test(name)
-
c = TestMethod.new(name)
-
@tests << c
-
c
-
end
-
-
# Whne used by a per-file runner.
-
#alias_method :file, :name
-
-
# Were there any errors?
-
1
def error?
-
count_errors != 0
-
end
-
-
# Were there any failures?
-
1
def fail?
-
count_failures != 0
-
end
-
-
# Did all tests/assertion pass?
-
1
def pass?
-
not(fail? or error?)
-
end
-
-
1
def count_tests
-
tests.size
-
end
-
-
1
alias_method :size, :count_tests
-
-
1
def count_failures
-
sum = 0; tests.each{ |t| sum += 1 if t.fail? }; sum
-
end
-
-
1
def count_errors
-
sum = 0; tests.each{ |t| sum += 1 if t.error? }; sum
-
end
-
-
1
def count_passes
-
sum = 0; tests.each{ |t| sum += 1 if t.pass? }; sum
-
end
-
-
1
def count_skips
-
# Why not use tests.select(&:skip?).size ?
-
sum = 0; tests.each{ |t| sum += 1 if t.skip? }; sum
-
end
-
-
#
-
1
def counts
-
return count_tests, count_assertions, count_failures, count_errors, count_skips
-
end
-
-
1
def message
-
tests.collect{ |t| t.message }.join("\n")
-
end
-
-
1
def each(&block)
-
tests.each(&block)
-
end
-
end
-
-
end
-
-
1
module Turn
-
-
#
-
1
class TestMethod
-
1
attr_accessor :name
-
1
attr_accessor :file
-
1
attr_accessor :raised
-
1
attr_accessor :message
-
1
attr_accessor :backtrace
-
-
1
def initialize(name)
-
@name = name
-
@fail = false
-
@error = false
-
@skip = false
-
@raised = nil
-
@message = nil
-
@backtrace = []
-
end
-
-
1
def fail!(assertion)
-
@fail, @error, @skip = true, false, false
-
@raised = assertion
-
@message = assertion.message
-
@backtrace = assertion.backtrace
-
end
-
-
1
def error!(exception)
-
@fail, @error, @skip = false, true, false
-
@raised = exception
-
@message = exception.message
-
@backtrace = exception.backtrace
-
end
-
-
1
def skip!(assertion)
-
@fail, @error, @skip = false, false, true
-
@raised = assertion
-
@message = assertion.message
-
@backtrace = assertion.backtrace
-
end
-
-
1
def fail? ; @fail ; end
-
1
def error? ; @error ; end
-
1
def skip? ; @skip ; end
-
-
# TODO: should this include `or @skip`?
-
1
def pass? ; !(@fail or @error) ; end
-
-
1
def to_s ; name ; end
-
end
-
-
end
-
-
1
module Turn
-
-
#
-
1
class TestSuite
-
-
1
include Enumerable
-
-
1
attr_accessor :name
-
1
attr_accessor :cases
-
1
attr_accessor :seed
-
-
# This one can be set manually since it
-
# is not calculatable (beyond the case level).
-
1
attr_writer :count_assertions
-
-
#
-
1
def initialize(name=nil)
-
@name = name
-
@cases = []
-
@size = nil
-
@seed = nil
-
-
#@count_tests = nil
-
#@count_assertions = nil
-
#@count_failures = nil
-
#@count_errors = nil
-
#@count_passes = nil
-
end
-
-
#
-
1
def new_case(name, *files)
-
c = TestCase.new(name, *files)
-
@cases << c
-
c
-
end
-
-
1
def count_tests
-
#@count_tests ||= (
-
sum = 0; each{ |c| sum += c.count_tests }; sum
-
#)
-
end
-
-
1
def count_assertions
-
#@count_assertions ||= (
-
sum = 0; each{ |c| sum += c.count_assertions }; sum
-
#)
-
end
-
-
1
def count_failures
-
#@count_failures ||= (
-
sum = 0; each{ |c| sum += c.count_failures }; sum
-
#)
-
end
-
-
1
def count_errors
-
#@count_errors ||= (
-
sum = 0; each{ |c| sum += c.count_errors }; sum
-
#)
-
end
-
-
1
def count_passes
-
#@count_passes ||= (
-
sum = 0; each{ |c| sum += c.count_passes }; sum
-
#)
-
end
-
-
1
def count_skips
-
sum = 0; each{ |c| sum += c.count_skips }; sum
-
end
-
-
# Convenience methods --this is what is typcially wanted.
-
1
def counts
-
return count_tests, count_assertions, count_failures, count_errors ,count_skips
-
end
-
-
1
def each(&block)
-
@cases.each(&block)
-
end
-
-
1
def size
-
@size ||= @cases.size
-
end
-
-
# Why the size would ever have to overridden... well who can say.
-
1
def size=(number)
-
@size = number.to_i
-
end
-
-
1
def passed?
-
(count_failures == 0 && count_errors == 0)
-
end
-
end
-
-
end
-
1
module Turn
-
-
# Configure Turn
-
1
def self.config(&block)
-
@config ||= Configuration.new
-
block.call(@config) if block
-
@config
-
end
-
-
# TODO: Support for test run loggging ?
-
-
# Central interface for Turn configuration.
-
#
-
1
class Configuration
-
-
# List of if file names or glob pattern of tests to run.
-
1
attr_reader :tests
-
-
# List of file names or globs to exclude from +tests+ list.
-
1
attr_reader :exclude
-
-
# Regexp pattern that all test name's must
-
# match to be eligible to run.
-
1
attr_accessor :pattern
-
-
# Regexp pattern that all test cases must
-
# match to be eligible to run.
-
1
attr_accessor :matchcase
-
-
# Add these folders to the $LOAD_PATH.
-
1
attr_reader :loadpath
-
-
# Libs to require when running tests.
-
1
attr_reader :requires
-
-
# Reporter type.
-
1
attr_accessor :format
-
-
# Run mode.
-
1
attr_accessor :runmode
-
-
# Test against live install (i.e. Don't use loadpath option)
-
1
attr_accessor :live
-
-
# Log results? May be true/false or log file name. (TODO)
-
1
attr_accessor :log
-
-
# Verbose output?
-
1
attr_accessor :verbose
-
-
# Runtime threshold.
-
1
attr_accessor :mark
-
-
# Test framework, either `:minitest` or `:testunit`.
-
# @todo Is this used any more?
-
1
attr_accessor :framework
-
-
# Enable full backtrace
-
1
attr_accessor :trace
-
-
# Use natural language case names.
-
1
attr_accessor :natural
-
-
#
-
1
def verbose?
-
@verbose
-
end
-
-
1
def live?
-
@live
-
end
-
-
1
def natural?
-
@natural
-
end
-
-
1
def ansi?
-
@ansi
-
end
-
-
1
private
-
-
1
def initialize
-
yield(self) if block_given?
-
initialize_defaults
-
end
-
-
#
-
1
def initialize_defaults
-
@loadpath ||= ['lib']
-
@tests ||= ["test/**/{test,}*{,test}.rb"]
-
@exclude ||= []
-
@requires ||= []
-
@live ||= false
-
@log ||= true
-
#@runner ||= RUBY_VERSION >= "1.9" ? MiniRunner : TestRunner
-
@matchcase ||= nil
-
@pattern ||= /.*/
-
@natural ||= false
-
@verbose ||= false
-
@format ||= environment_format
-
@trace ||= environment_trace
-
@ansi ||= environment_ansi
-
-
@files = nil # reset files just in case
-
end
-
-
# Collect test configuation.
-
#def test_configuration(options={})
-
# #options = configure_options(options, 'test')
-
# #options['loadpath'] ||= metadata.loadpath
-
# options['tests'] ||= self.tests
-
# options['loadpath'] ||= self.loadpath
-
# options['requires'] ||= self.requires
-
# options['live'] ||= self.live
-
# options['exclude'] ||= self.exclude
-
# #options['tests'] = list_option(options['tests'])
-
# options['loadpath'] = list_option(options['loadpath'])
-
# options['exclude'] = list_option(options['exclude'])
-
# options['require'] = list_option(options['require'])
-
# return options
-
#end
-
-
#
-
1
def list_option(list)
-
case list
-
when nil
-
[]
-
when Array
-
list
-
else
-
list.split(/[:;]/)
-
end
-
end
-
-
1
public
-
-
1
def ansi=(boolean)
-
@ansi = boolean ? true : false
-
end
-
-
1
def tests=(paths)
-
@tests = list_option(paths)
-
end
-
-
1
def loadpath=(paths)
-
@loadpath = list_option(paths)
-
end
-
-
1
def exclude=(paths)
-
@exclude = list_option(paths)
-
end
-
-
1
def requires=(paths)
-
@requires = list_option(paths)
-
end
-
-
# Test files.
-
1
def files
-
@files ||= (
-
fs = tests.map do |t|
-
File.directory?(t) ? Dir[File.join(t, '**', '*')] : Dir[t]
-
end
-
fs = fs.flatten.reject{ |f| File.directory?(f) }
-
-
ex = exclude.map do |x|
-
File.directory?(x) ? Dir[File.join(x, '**', '*')] : Dir[x]
-
end
-
ex = ex.flatten.reject{ |f| File.directory?(f) }
-
-
(fs - ex).uniq.map{ |f| File.expand_path(f) }
-
).flatten
-
end
-
-
# TODO: Better name ?
-
1
def suite_name
-
files.map{ |path| File.dirname(path).sub(Dir.pwd+'/','') }.uniq.join(',')
-
end
-
-
# Select reporter based on output mode.
-
1
def reporter
-
@reporter ||= (
-
opts = reporter_options
-
case format
-
when :marshal
-
require 'turn/reporters/marshal_reporter'
-
Turn::MarshalReporter.new($stdout, opts)
-
when :progress
-
require 'turn/reporters/progress_reporter'
-
Turn::ProgressReporter.new($stdout, opts)
-
when :dotted, :dot
-
require 'turn/reporters/dot_reporter'
-
Turn::DotReporter.new($stdout, opts)
-
when :outline
-
require 'turn/reporters/outline_reporter'
-
Turn::OutlineReporter.new($stdout, opts)
-
when :cue
-
require 'turn/reporters/cue_reporter'
-
Turn::CueReporter.new($stdout, opts)
-
when :pretty
-
require 'turn/reporters/pretty_reporter'
-
Turn::PrettyReporter.new($stdout, opts)
-
else
-
require 'turn/reporters/pretty_reporter'
-
Turn::PrettyReporter.new($stdout, opts)
-
end
-
)
-
end
-
-
#
-
1
def reporter_options
-
{ :trace=>trace, :natural=>natural?, :verbose=>verbose?, :mark=>mark }
-
end
-
-
#
-
1
def environment_format
-
ENV['rpt']
-
end
-
-
#
-
1
def environment_trace
-
(ENV['backtrace'] ? ENV['backtrace'].to_i : nil)
-
end
-
-
#
-
1
def environment_ansi
-
case ENV['ansi']
-
when 'true','yes','on'
-
true
-
when 'false','no','off'
-
false
-
else
-
nil
-
end
-
end
-
-
end
-
-
end
-
1
module Turn
-
-
# Controls execution of test run.
-
#
-
1
class Controller
-
-
#
-
1
def initialize(config=Turn.config)
-
@config = config
-
end
-
-
#
-
1
attr :config
-
-
#
-
1
def start
-
if config.files.empty?
-
$stderr.puts "No tests."
-
return
-
end
-
-
setup
-
-
testrun = runner.new
-
testrun.start
-
end
-
-
#
-
1
def setup
-
config.loadpath.each{ |path| $: << path } unless config.live?
-
config.requires.each{ |path| require(path) }
-
config.files.each{ |path| require(path) }
-
end
-
-
# Insatance of Runner, selected based on format and runmode.
-
1
def runner
-
@runner ||= (
-
require 'turn/runners/minirunner'
-
-
case config.runmode
-
when :marshal
-
Turn::MiniRunner
-
when :solo
-
require 'turn/runners/solorunner'
-
Turn::SoloRunner
-
when :cross
-
require 'turn/runners/crossrunner'
-
Turn::CrossRunner
-
else
-
Turn::MiniRunner
-
end
-
)
-
end
-
-
end
-
-
end
-
1
module Turn
-
1
VERSION = "0.9.5"
-
end
-
#--
-
# Copyright (c) 2005-2010 Philip Ross
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
-
# of this software and associated documentation files (the "Software"), to deal
-
# in the Software without restriction, including without limitation the rights
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-
# copies of the Software, and to permit persons to whom the Software is
-
# furnished to do so, subject to the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be included in all
-
# copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-
# THE SOFTWARE.
-
#++
-
-
# Add the directory containing this file to the start of the load path if it
-
# isn't there already.
-
$:.unshift(File.dirname(__FILE__)) unless
-
1
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
-
-
-
1
require 'tzinfo/ruby_core_support'
-
1
require 'tzinfo/offset_rationals'
-
1
require 'tzinfo/time_or_datetime'
-
-
1
require 'tzinfo/timezone_definition'
-
-
1
require 'tzinfo/timezone_offset_info'
-
1
require 'tzinfo/timezone_transition_info'
-
-
1
require 'tzinfo/timezone_index_definition'
-
-
1
require 'tzinfo/timezone_info'
-
1
require 'tzinfo/data_timezone_info'
-
1
require 'tzinfo/linked_timezone_info'
-
-
1
require 'tzinfo/timezone_period'
-
1
require 'tzinfo/timezone'
-
1
require 'tzinfo/info_timezone'
-
1
require 'tzinfo/data_timezone'
-
1
require 'tzinfo/linked_timezone'
-
1
require 'tzinfo/timezone_proxy'
-
-
1
require 'tzinfo/country_index_definition'
-
1
require 'tzinfo/country_info'
-
-
1
require 'tzinfo/country'
-
1
require 'tzinfo/country_timezone'
-
#--
-
# Copyright (c) 2005-2010 Philip Ross
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
-
# of this software and associated documentation files (the "Software"), to deal
-
# in the Software without restriction, including without limitation the rights
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-
# copies of the Software, and to permit persons to whom the Software is
-
# furnished to do so, subject to the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be included in all
-
# copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-
# THE SOFTWARE.
-
#++
-
-
1
module TZInfo
-
# Thrown by Country#get if the code given is not valid.
-
1
class InvalidCountryCode < StandardError
-
end
-
-
# An ISO 3166 country. Can be used to get a list of Timezones for a country.
-
# For example:
-
#
-
# us = Country.get('US')
-
# us.zone_identifiers
-
# us.zones
-
# us.zone_info
-
1
class Country
-
1
include Comparable
-
-
# Defined countries.
-
1
@@countries = {}
-
-
# Whether the countries index has been loaded yet.
-
1
@@index_loaded = false
-
-
# Gets a Country by its ISO 3166 code. Raises an InvalidCountryCode
-
# exception if it couldn't be found.
-
1
def self.get(identifier)
-
instance = @@countries[identifier]
-
-
unless instance
-
load_index
-
info = Indexes::Countries.countries[identifier]
-
raise InvalidCountryCode.new, 'Invalid identifier' unless info
-
instance = Country.new(info)
-
@@countries[identifier] = instance
-
end
-
-
instance
-
end
-
-
# If identifier is a CountryInfo object, initializes the Country instance,
-
# otherwise calls get(identifier).
-
1
def self.new(identifier)
-
if identifier.kind_of?(CountryInfo)
-
instance = super()
-
instance.send :setup, identifier
-
instance
-
else
-
get(identifier)
-
end
-
end
-
-
# Returns an Array of all the valid country codes.
-
1
def self.all_codes
-
load_index
-
Indexes::Countries.countries.keys
-
end
-
-
# Returns an Array of all the defined Countries.
-
1
def self.all
-
load_index
-
Indexes::Countries.countries.keys.collect {|code| get(code)}
-
end
-
-
# The ISO 3166 country code.
-
1
def code
-
@info.code
-
end
-
-
# The name of the country.
-
1
def name
-
@info.name
-
end
-
-
# Alias for name.
-
1
def to_s
-
name
-
end
-
-
# Returns internal object state as a programmer-readable string.
-
1
def inspect
-
"#<#{self.class}: #{@info.code}>"
-
end
-
-
# Returns a frozen array of all the zone identifiers for the country. These
-
# are in an order that
-
# (1) makes some geographical sense, and
-
# (2) puts the most populous zones first, where that does not contradict (1).
-
1
def zone_identifiers
-
@info.zone_identifiers
-
end
-
1
alias zone_names zone_identifiers
-
-
# An array of all the Timezones for this country. Returns TimezoneProxy
-
# objects to avoid the overhead of loading Timezone definitions until
-
# a conversion is actually required. The Timezones are returned in an order
-
# that
-
# (1) makes some geographical sense, and
-
# (2) puts the most populous zones first, where that does not contradict (1).
-
1
def zones
-
zone_identifiers.collect {|id|
-
Timezone.get_proxy(id)
-
}
-
end
-
-
# Returns a frozen array of all the timezones for the for the country as
-
# CountryTimezone instances (containing extra information about each zone).
-
# These are in an order that
-
# (1) makes some geographical sense, and
-
# (2) puts the most populous zones first, where that does not contradict (1).
-
1
def zone_info
-
@info.zones
-
end
-
-
# Compare two Countries based on their code. Returns -1 if c is less
-
# than self, 0 if c is equal to self and +1 if c is greater than self.
-
1
def <=>(c)
-
code <=> c.code
-
end
-
-
# Returns true if and only if the code of c is equal to the code of this
-
# Country.
-
1
def eql?(c)
-
self == c
-
end
-
-
# Returns a hash value for this Country.
-
1
def hash
-
code.hash
-
end
-
-
# Dumps this Country for marshalling.
-
1
def _dump(limit)
-
code
-
end
-
-
# Loads a marshalled Country.
-
1
def self._load(data)
-
Country.get(data)
-
end
-
-
1
private
-
# Loads in the index of countries if it hasn't already been loaded.
-
1
def self.load_index
-
unless @@index_loaded
-
require 'tzinfo/indexes/countries'
-
@@index_loaded = true
-
end
-
end
-
-
# Called by Country.new to initialize a new Country instance. The info
-
# parameter is a CountryInfo that defines the country.
-
1
def setup(info)
-
@info = info
-
end
-
end
-
end
-
#--
-
# Copyright (c) 2006-2010 Philip Ross
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
-
# of this software and associated documentation files (the "Software"), to deal
-
# in the Software without restriction, including without limitation the rights
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-
# copies of the Software, and to permit persons to whom the Software is
-
# furnished to do so, subject to the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be included in all
-
# copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-
# THE SOFTWARE.
-
#++
-
-
1
module TZInfo
-
# The country index file includes CountryIndexDefinition which provides
-
# a country method used to define each country in the index.
-
1
module CountryIndexDefinition #:nodoc:
-
1
def self.append_features(base)
-
super
-
base.extend(ClassMethods)
-
base.instance_eval { @countries = {} }
-
end
-
-
1
module ClassMethods #:nodoc:
-
# Defines a country with an ISO 3166 country code, name and block. The
-
# block will be evaluated to obtain all the timezones for the country.
-
# Calls Country.country_defined with the definition of each country.
-
1
def country(code, name, &block)
-
@countries[code] = CountryInfo.new(code, name, &block)
-
end
-
-
# Returns a frozen hash of all the countries that have been defined in
-
# the index.
-
1
def countries
-
@countries.freeze
-
end
-
end
-
end
-
end
-
#--
-
# Copyright (c) 2006-2010 Philip Ross
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
-
# of this software and associated documentation files (the "Software"), to deal
-
# in the Software without restriction, including without limitation the rights
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-
# copies of the Software, and to permit persons to whom the Software is
-
# furnished to do so, subject to the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be included in all
-
# copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-
# THE SOFTWARE.
-
#++
-
-
1
module TZInfo
-
# Class to store the data loaded from the country index. Instances of this
-
# class are passed to the blocks in the index that define timezones.
-
1
class CountryInfo #:nodoc:
-
1
attr_reader :code
-
1
attr_reader :name
-
-
# Constructs a new CountryInfo with an ISO 3166 country code, name and
-
# block. The block will be evaluated to obtain the timezones for the country
-
# (when they are first needed).
-
1
def initialize(code, name, &block)
-
@code = code
-
@name = name
-
@block = block
-
@zones = nil
-
@zone_identifiers = nil
-
end
-
-
# Called by the index data to define a timezone for the country.
-
1
def timezone(identifier, latitude_numerator, latitude_denominator,
-
longitude_numerator, longitude_denominator, description = nil)
-
# Currently only store the identifiers.
-
@zones << CountryTimezone.new(identifier, latitude_numerator,
-
latitude_denominator, longitude_numerator, longitude_denominator,
-
description)
-
end
-
-
# Returns a frozen array of all the zone identifiers for the country. These
-
# are in the order they were added using the timezone method.
-
1
def zone_identifiers
-
unless @zone_identifiers
-
@zone_identifiers = zones.collect {|zone| zone.identifier}
-
@zone_identifiers.freeze
-
end
-
-
@zone_identifiers
-
end
-
-
# Returns internal object state as a programmer-readable string.
-
1
def inspect
-
"#<#{self.class}: #@code>"
-
end
-
-
# Returns a frozen array of all the timezones for the for the country as
-
# CountryTimezone instances. These are in the order they were added using
-
# the timezone method.
-
1
def zones
-
unless @zones
-
@zones = []
-
@block.call(self) if @block
-
@block = nil
-
@zones.freeze
-
end
-
-
@zones
-
end
-
end
-
end
-
#--
-
# Copyright (c) 2006-2010 Philip Ross
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
-
# of this software and associated documentation files (the "Software"), to deal
-
# in the Software without restriction, including without limitation the rights
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-
# copies of the Software, and to permit persons to whom the Software is
-
# furnished to do so, subject to the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be included in all
-
# copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-
# THE SOFTWARE.
-
#++
-
-
1
module TZInfo
-
# A Timezone within a Country. This contains extra information about the
-
# Timezone that is specific to the Country (a Timezone could be used by
-
# multiple countries).
-
1
class CountryTimezone
-
# The zone identifier.
-
1
attr_reader :identifier
-
-
# A description of this timezone in relation to the country, e.g.
-
# "Eastern Time". This is usually nil for countries having only a single
-
# Timezone.
-
1
attr_reader :description
-
-
# Creates a new CountryTimezone with a timezone identifier, latitude,
-
# longitude and description. The latitude and longitude are specified as
-
# rationals - a numerator and denominator. For performance reasons, the
-
# numerators and denominators must be specified in their lowest form.
-
#
-
# CountryTimezone instances should not normally be constructed manually.
-
1
def initialize(identifier, latitude_numerator, latitude_denominator,
-
longitude_numerator, longitude_denominator, description = nil) #:nodoc:
-
@identifier = identifier
-
@latitude_numerator = latitude_numerator
-
@latitude_denominator = latitude_denominator
-
@longitude_numerator = longitude_numerator
-
@longitude_denominator = longitude_denominator
-
@description = description
-
end
-
-
# The Timezone (actually a TimezoneProxy for performance reasons).
-
1
def timezone
-
Timezone.get_proxy(@identifier)
-
end
-
-
# if description is not nil, this method returns description; otherwise it
-
# returns timezone.friendly_identifier(true).
-
1
def description_or_friendly_identifier
-
description || timezone.friendly_identifier(true)
-
end
-
-
# The latitude of this timezone in degrees as a Rational.
-
1
def latitude
-
@latitude ||= RubyCoreSupport.rational_new!(@latitude_numerator, @latitude_denominator)
-
end
-
-
# The longitude of this timezone in degrees as a Rational.
-
1
def longitude
-
@longitude ||= RubyCoreSupport.rational_new!(@longitude_numerator, @longitude_denominator)
-
end
-
-
# Returns true if and only if the given CountryTimezone is equal to the
-
# current CountryTimezone (has the same identifer, latitude, longitude
-
# and description).
-
1
def ==(ct)
-
ct.respond_to?(:identifier) && ct.respond_to?(:latitude) &&
-
ct.respond_to?(:longitude) && ct.respond_to?(:description) &&
-
identifier == ct.identifier && latitude == ct.latitude &&
-
longitude == ct.longitude && description == ct.description
-
end
-
-
# Returns true if and only if the given CountryTimezone is equal to the
-
# current CountryTimezone (has the same identifer, latitude, longitude
-
# and description).
-
1
def eql?(ct)
-
self == ct
-
end
-
-
# Returns a hash of this CountryTimezone.
-
1
def hash
-
@identifier.hash ^ @latitude_numerator.hash ^ @latitude_denominator.hash ^
-
@longitude_numerator.hash ^ @longitude_denominator.hash ^ @description.hash
-
end
-
-
# Returns internal object state as a programmer-readable string.
-
1
def inspect
-
"#<#{self.class}: #@identifier>"
-
end
-
end
-
end
-
#--
-
# Copyright (c) 2006-2010 Philip Ross
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
-
# of this software and associated documentation files (the "Software"), to deal
-
# in the Software without restriction, including without limitation the rights
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-
# copies of the Software, and to permit persons to whom the Software is
-
# furnished to do so, subject to the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be included in all
-
# copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-
# THE SOFTWARE.
-
#++
-
-
1
module TZInfo
-
-
# A Timezone based on a DataTimezoneInfo.
-
1
class DataTimezone < InfoTimezone #:nodoc:
-
-
# Returns the TimezonePeriod for the given UTC time. utc can either be
-
# a DateTime, Time or integer timestamp (Time.to_i). Any timezone
-
# information in utc is ignored (it is treated as a UTC time).
-
#
-
# If no TimezonePeriod could be found, PeriodNotFound is raised.
-
1
def period_for_utc(utc)
-
info.period_for_utc(utc)
-
end
-
-
# Returns the set of TimezonePeriod instances that are valid for the given
-
# local time as an array. If you just want a single period, use
-
# period_for_local instead and specify how abiguities should be resolved.
-
# Raises PeriodNotFound if no periods are found for the given time.
-
1
def periods_for_local(local)
-
info.periods_for_local(local)
-
end
-
end
-
end
-
#--
-
# Copyright (c) 2006-2010 Philip Ross
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
-
# of this software and associated documentation files (the "Software"), to deal
-
# in the Software without restriction, including without limitation the rights
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-
# copies of the Software, and to permit persons to whom the Software is
-
# furnished to do so, subject to the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be included in all
-
# copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-
# THE SOFTWARE.
-
#++
-
-
1
module TZInfo
-
# Thrown if no offsets have been defined when calling period_for_utc or
-
# periods_for_local. Indicates an error in the timezone data.
-
1
class NoOffsetsDefined < StandardError
-
end
-
-
# Represents a (non-linked) timezone defined in a data module.
-
1
class DataTimezoneInfo < TimezoneInfo #:nodoc:
-
-
# Constructs a new TimezoneInfo with its identifier.
-
1
def initialize(identifier)
-
super(identifier)
-
@offsets = {}
-
@transitions = []
-
@previous_offset = nil
-
@transitions_index = nil
-
end
-
-
# Defines a offset. The id uniquely identifies this offset within the
-
# timezone. utc_offset and std_offset define the offset in seconds of
-
# standard time from UTC and daylight savings from standard time
-
# respectively. abbreviation describes the timezone offset (e.g. GMT, BST,
-
# EST or EDT).
-
#
-
# The first offset to be defined is treated as the offset that applies
-
# until the first transition. This will usually be in Local Mean Time (LMT).
-
#
-
# ArgumentError will be raised if the id is already defined.
-
1
def offset(id, utc_offset, std_offset, abbreviation)
-
raise ArgumentError, 'Offset already defined' if @offsets.has_key?(id)
-
-
offset = TimezoneOffsetInfo.new(utc_offset, std_offset, abbreviation)
-
@offsets[id] = offset
-
@previous_offset = offset unless @previous_offset
-
end
-
-
# Defines a transition. Transitions must be defined in chronological order.
-
# ArgumentError will be raised if a transition is added out of order.
-
# offset_id refers to an id defined with offset. ArgumentError will be
-
# raised if the offset_id cannot be found. numerator_or_time and
-
# denomiator specify the time the transition occurs as. See
-
# TimezoneTransitionInfo for more detail about specifying times.
-
1
def transition(year, month, offset_id, numerator_or_time, denominator = nil)
-
offset = @offsets[offset_id]
-
raise ArgumentError, 'Offset not found' unless offset
-
-
if @transitions_index
-
if year < @last_year || (year == @last_year && month < @last_month)
-
raise ArgumentError, 'Transitions must be increasing date order'
-
end
-
-
# Record the position of the first transition with this index.
-
index = transition_index(year, month)
-
@transitions_index[index] ||= @transitions.length
-
-
# Fill in any gaps
-
(index - 1).downto(0) do |i|
-
break if @transitions_index[i]
-
@transitions_index[i] = @transitions.length
-
end
-
else
-
@transitions_index = [@transitions.length]
-
@start_year = year
-
@start_month = month
-
end
-
-
@transitions << TimezoneTransitionInfo.new(offset, @previous_offset,
-
numerator_or_time, denominator)
-
@last_year = year
-
@last_month = month
-
@previous_offset = offset
-
end
-
-
# Returns the TimezonePeriod for the given UTC time.
-
# Raises NoOffsetsDefined if no offsets have been defined.
-
1
def period_for_utc(utc)
-
unless @transitions.empty?
-
utc = TimeOrDateTime.wrap(utc)
-
index = transition_index(utc.year, utc.mon)
-
-
start_transition = nil
-
start = transition_before_end(index)
-
if start
-
start.downto(0) do |i|
-
if @transitions[i].at <= utc
-
start_transition = @transitions[i]
-
break
-
end
-
end
-
end
-
-
end_transition = nil
-
start = transition_after_start(index)
-
if start
-
start.upto(@transitions.length - 1) do |i|
-
if @transitions[i].at > utc
-
end_transition = @transitions[i]
-
break
-
end
-
end
-
end
-
-
if start_transition || end_transition
-
TimezonePeriod.new(start_transition, end_transition)
-
else
-
# Won't happen since there are transitions. Must always find one
-
# transition that is either >= or < the specified time.
-
raise 'No transitions found in search'
-
end
-
else
-
raise NoOffsetsDefined, 'No offsets have been defined' unless @previous_offset
-
TimezonePeriod.new(nil, nil, @previous_offset)
-
end
-
end
-
-
# Returns the set of TimezonePeriods for the given local time as an array.
-
# Results returned are ordered by increasing UTC start date.
-
# Returns an empty array if no periods are found for the given time.
-
# Raises NoOffsetsDefined if no offsets have been defined.
-
1
def periods_for_local(local)
-
unless @transitions.empty?
-
local = TimeOrDateTime.wrap(local)
-
index = transition_index(local.year, local.mon)
-
-
result = []
-
-
start_index = transition_after_start(index - 1)
-
if start_index && @transitions[start_index].local_end > local
-
if start_index > 0
-
if @transitions[start_index - 1].local_start <= local
-
result << TimezonePeriod.new(@transitions[start_index - 1], @transitions[start_index])
-
end
-
else
-
result << TimezonePeriod.new(nil, @transitions[start_index])
-
end
-
end
-
-
end_index = transition_before_end(index + 1)
-
-
if end_index
-
start_index = end_index unless start_index
-
-
start_index.upto(transition_before_end(index + 1)) do |i|
-
if @transitions[i].local_start <= local
-
if i + 1 < @transitions.length
-
if @transitions[i + 1].local_end > local
-
result << TimezonePeriod.new(@transitions[i], @transitions[i + 1])
-
end
-
else
-
result << TimezonePeriod.new(@transitions[i], nil)
-
end
-
end
-
end
-
end
-
-
result
-
else
-
raise NoOffsetsDefined, 'No offsets have been defined' unless @previous_offset
-
[TimezonePeriod.new(nil, nil, @previous_offset)]
-
end
-
end
-
-
1
private
-
# Returns the index into the @transitions_index array for a given year
-
# and month.
-
1
def transition_index(year, month)
-
index = (year - @start_year) * 2
-
index += 1 if month > 6
-
index -= 1 if @start_month > 6
-
index
-
end
-
-
# Returns the index into @transitions of the first transition that occurs
-
# on or after the start of the given index into @transitions_index.
-
# Returns nil if there are no such transitions.
-
1
def transition_after_start(index)
-
if index >= @transitions_index.length
-
nil
-
else
-
index = 0 if index < 0
-
@transitions_index[index]
-
end
-
end
-
-
# Returns the index into @transitions of the first transition that occurs
-
# before the end of the given index into @transitions_index.
-
# Returns nil if there are no such transitions.
-
1
def transition_before_end(index)
-
index = index + 1
-
-
if index <= 0
-
nil
-
elsif index >= @transitions_index.length
-
@transitions.length - 1
-
else
-
@transitions_index[index] - 1
-
end
-
end
-
end
-
end
-
#--
-
# Copyright (c) 2006-2010 Philip Ross
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
-
# of this software and associated documentation files (the "Software"), to deal
-
# in the Software without restriction, including without limitation the rights
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-
# copies of the Software, and to permit persons to whom the Software is
-
# furnished to do so, subject to the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be included in all
-
# copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-
# THE SOFTWARE.
-
#++
-
-
1
module TZInfo
-
-
# A Timezone based on a TimezoneInfo.
-
1
class InfoTimezone < Timezone #:nodoc:
-
-
# Constructs a new InfoTimezone with a TimezoneInfo instance.
-
1
def self.new(info)
-
tz = super()
-
tz.send(:setup, info)
-
tz
-
end
-
-
# The identifier of the timezone, e.g. "Europe/Paris".
-
1
def identifier
-
@info.identifier
-
end
-
-
1
protected
-
# The TimezoneInfo for this Timezone.
-
1
def info
-
@info
-
end
-
-
1
def setup(info)
-
@info = info
-
end
-
end
-
end
-
#--
-
# Copyright (c) 2006-2010 Philip Ross
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
-
# of this software and associated documentation files (the "Software"), to deal
-
# in the Software without restriction, including without limitation the rights
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-
# copies of the Software, and to permit persons to whom the Software is
-
# furnished to do so, subject to the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be included in all
-
# copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-
# THE SOFTWARE.
-
#++
-
-
1
module TZInfo
-
-
1
class LinkedTimezone < InfoTimezone #:nodoc:
-
# Returns the TimezonePeriod for the given UTC time. utc can either be
-
# a DateTime, Time or integer timestamp (Time.to_i). Any timezone
-
# information in utc is ignored (it is treated as a UTC time).
-
#
-
# If no TimezonePeriod could be found, PeriodNotFound is raised.
-
1
def period_for_utc(utc)
-
@linked_timezone.period_for_utc(utc)
-
end
-
-
# Returns the set of TimezonePeriod instances that are valid for the given
-
# local time as an array. If you just want a single period, use
-
# period_for_local instead and specify how abiguities should be resolved.
-
# Raises PeriodNotFound if no periods are found for the given time.
-
1
def periods_for_local(local)
-
@linked_timezone.periods_for_local(local)
-
end
-
-
1
protected
-
1
def setup(info)
-
super(info)
-
@linked_timezone = Timezone.get(info.link_to_identifier)
-
end
-
end
-
end
-
#--
-
# Copyright (c) 2006-2010 Philip Ross
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
-
# of this software and associated documentation files (the "Software"), to deal
-
# in the Software without restriction, including without limitation the rights
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-
# copies of the Software, and to permit persons to whom the Software is
-
# furnished to do so, subject to the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be included in all
-
# copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-
# THE SOFTWARE.
-
#++
-
-
1
module TZInfo
-
# Represents a linked timezone defined in a data module.
-
1
class LinkedTimezoneInfo < TimezoneInfo #:nodoc:
-
-
# The zone that provides the data (that this zone is an alias for).
-
1
attr_reader :link_to_identifier
-
-
# Constructs a new TimezoneInfo with an identifier and the identifier
-
# of the zone linked to.
-
1
def initialize(identifier, link_to_identifier)
-
super(identifier)
-
@link_to_identifier = link_to_identifier
-
end
-
-
# Returns internal object state as a programmer-readable string.
-
1
def inspect
-
"#<#{self.class}: #@identifier,#@link_to_identifier>"
-
end
-
end
-
end
-
#--
-
# Copyright (c) 2006-2010 Philip Ross
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
-
# of this software and associated documentation files (the "Software"), to deal
-
# in the Software without restriction, including without limitation the rights
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-
# copies of the Software, and to permit persons to whom the Software is
-
# furnished to do so, subject to the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be included in all
-
# copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-
# THE SOFTWARE.
-
#++
-
-
1
require 'rational' unless defined?(Rational)
-
-
1
module TZInfo
-
-
# Provides a method for getting Rationals for a timezone offset in seconds.
-
# Pre-reduced rationals are returned for all the half-hour intervals between
-
# -14 and +14 hours to avoid having to call gcd at runtime.
-
1
module OffsetRationals #:nodoc:
-
1
@@rational_cache = {
-
-50400 => RubyCoreSupport.rational_new!(-7,12),
-
-48600 => RubyCoreSupport.rational_new!(-9,16),
-
-46800 => RubyCoreSupport.rational_new!(-13,24),
-
-45000 => RubyCoreSupport.rational_new!(-25,48),
-
-43200 => RubyCoreSupport.rational_new!(-1,2),
-
-41400 => RubyCoreSupport.rational_new!(-23,48),
-
-39600 => RubyCoreSupport.rational_new!(-11,24),
-
-37800 => RubyCoreSupport.rational_new!(-7,16),
-
-36000 => RubyCoreSupport.rational_new!(-5,12),
-
-34200 => RubyCoreSupport.rational_new!(-19,48),
-
-32400 => RubyCoreSupport.rational_new!(-3,8),
-
-30600 => RubyCoreSupport.rational_new!(-17,48),
-
-28800 => RubyCoreSupport.rational_new!(-1,3),
-
-27000 => RubyCoreSupport.rational_new!(-5,16),
-
-25200 => RubyCoreSupport.rational_new!(-7,24),
-
-23400 => RubyCoreSupport.rational_new!(-13,48),
-
-21600 => RubyCoreSupport.rational_new!(-1,4),
-
-19800 => RubyCoreSupport.rational_new!(-11,48),
-
-18000 => RubyCoreSupport.rational_new!(-5,24),
-
-16200 => RubyCoreSupport.rational_new!(-3,16),
-
-14400 => RubyCoreSupport.rational_new!(-1,6),
-
-12600 => RubyCoreSupport.rational_new!(-7,48),
-
-10800 => RubyCoreSupport.rational_new!(-1,8),
-
-9000 => RubyCoreSupport.rational_new!(-5,48),
-
-7200 => RubyCoreSupport.rational_new!(-1,12),
-
-5400 => RubyCoreSupport.rational_new!(-1,16),
-
-3600 => RubyCoreSupport.rational_new!(-1,24),
-
-1800 => RubyCoreSupport.rational_new!(-1,48),
-
0 => RubyCoreSupport.rational_new!(0,1),
-
1800 => RubyCoreSupport.rational_new!(1,48),
-
3600 => RubyCoreSupport.rational_new!(1,24),
-
5400 => RubyCoreSupport.rational_new!(1,16),
-
7200 => RubyCoreSupport.rational_new!(1,12),
-
9000 => RubyCoreSupport.rational_new!(5,48),
-
10800 => RubyCoreSupport.rational_new!(1,8),
-
12600 => RubyCoreSupport.rational_new!(7,48),
-
14400 => RubyCoreSupport.rational_new!(1,6),
-
16200 => RubyCoreSupport.rational_new!(3,16),
-
18000 => RubyCoreSupport.rational_new!(5,24),
-
19800 => RubyCoreSupport.rational_new!(11,48),
-
21600 => RubyCoreSupport.rational_new!(1,4),
-
23400 => RubyCoreSupport.rational_new!(13,48),
-
25200 => RubyCoreSupport.rational_new!(7,24),
-
27000 => RubyCoreSupport.rational_new!(5,16),
-
28800 => RubyCoreSupport.rational_new!(1,3),
-
30600 => RubyCoreSupport.rational_new!(17,48),
-
32400 => RubyCoreSupport.rational_new!(3,8),
-
34200 => RubyCoreSupport.rational_new!(19,48),
-
36000 => RubyCoreSupport.rational_new!(5,12),
-
37800 => RubyCoreSupport.rational_new!(7,16),
-
39600 => RubyCoreSupport.rational_new!(11,24),
-
41400 => RubyCoreSupport.rational_new!(23,48),
-
43200 => RubyCoreSupport.rational_new!(1,2),
-
45000 => RubyCoreSupport.rational_new!(25,48),
-
46800 => RubyCoreSupport.rational_new!(13,24),
-
48600 => RubyCoreSupport.rational_new!(9,16),
-
50400 => RubyCoreSupport.rational_new!(7,12)}
-
-
# Returns a Rational expressing the fraction of a day that offset in
-
# seconds represents (i.e. equivalent to Rational(offset, 86400)).
-
1
def rational_for_offset(offset)
-
@@rational_cache[offset] || Rational(offset, 86400)
-
end
-
1
module_function :rational_for_offset
-
end
-
end
-
#--
-
# Copyright (c) 2008-2010 Philip Ross
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
-
# of this software and associated documentation files (the "Software"), to deal
-
# in the Software without restriction, including without limitation the rights
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-
# copies of the Software, and to permit persons to whom the Software is
-
# furnished to do so, subject to the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be included in all
-
# copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-
# THE SOFTWARE.
-
#++
-
-
1
require 'date'
-
1
require 'rational' unless defined?(Rational)
-
-
1
module TZInfo
-
-
# Methods to support different versions of Ruby.
-
1
module RubyCoreSupport #:nodoc:
-
-
# Use Rational.new! for performance reasons in Ruby 1.8.
-
# This has been removed from 1.9, but Rational performs better.
-
1
if Rational.respond_to? :new!
-
def self.rational_new!(numerator, denominator = 1)
-
Rational.new!(numerator, denominator)
-
end
-
else
-
1
def self.rational_new!(numerator, denominator = 1)
-
57
Rational(numerator, denominator)
-
end
-
end
-
-
# Ruby 1.8.6 introduced new! and deprecated new0.
-
# Ruby 1.9.0 removed new0.
-
# Ruby trunk revision 31668 removed the new! method.
-
# Still support new0 for better performance on older versions of Ruby (new0 indicates
-
# that the rational has already been reduced to its lowest terms).
-
# Fallback to jd with conversion from ajd if new! and new0 are unavailable.
-
1
if DateTime.respond_to? :new!
-
1
def self.datetime_new!(ajd = 0, of = 0, sg = Date::ITALY)
-
DateTime.new!(ajd, of, sg)
-
end
-
elsif DateTime.respond_to? :new0
-
def self.datetime_new!(ajd = 0, of = 0, sg = Date::ITALY)
-
DateTime.new0(ajd, of, sg)
-
end
-
else
-
HALF_DAYS_IN_DAY = rational_new!(1, 2)
-
-
def self.datetime_new!(ajd = 0, of = 0, sg = Date::ITALY)
-
# Convert from an Astronomical Julian Day number to a civil Julian Day number.
-
jd = ajd + of + HALF_DAYS_IN_DAY
-
-
# Ruby trunk revision 31862 changed the behaviour of DateTime.jd so that it will no
-
# longer accept a fractional civil Julian Day number if further arguments are specified.
-
# Calculate the hours, minutes and seconds to pass to jd.
-
-
jd_i = jd.to_i
-
jd_i -= 1 if jd < 0
-
hours = (jd - jd_i) * 24
-
hours_i = hours.to_i
-
minutes = (hours - hours_i) * 60
-
minutes_i = minutes.to_i
-
seconds = (minutes - minutes_i) * 60
-
-
DateTime.jd(jd_i, hours_i, minutes_i, seconds, of, sg)
-
end
-
end
-
end
-
end
-
#--
-
# Copyright (c) 2006-2010 Philip Ross
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
-
# of this software and associated documentation files (the "Software"), to deal
-
# in the Software without restriction, including without limitation the rights
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-
# copies of the Software, and to permit persons to whom the Software is
-
# furnished to do so, subject to the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be included in all
-
# copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-
# THE SOFTWARE.
-
#++
-
-
1
require 'date'
-
1
require 'time'
-
-
1
module TZInfo
-
# Used by TZInfo internally to represent either a Time, DateTime or integer
-
# timestamp (seconds since 1970-01-01 00:00:00).
-
1
class TimeOrDateTime #:nodoc:
-
1
include Comparable
-
-
# Constructs a new TimeOrDateTime. timeOrDateTime can be a Time, DateTime
-
# or an integer. If using a Time or DateTime, any time zone information is
-
# ignored.
-
1
def initialize(timeOrDateTime)
-
@time = nil
-
@datetime = nil
-
@timestamp = nil
-
-
if timeOrDateTime.is_a?(Time)
-
@time = timeOrDateTime
-
@time = Time.utc(@time.year, @time.mon, @time.mday, @time.hour, @time.min, @time.sec) unless @time.zone == 'UTC'
-
@orig = @time
-
elsif timeOrDateTime.is_a?(DateTime)
-
@datetime = timeOrDateTime
-
@datetime = @datetime.new_offset(0) unless @datetime.offset == 0
-
@orig = @datetime
-
else
-
@timestamp = timeOrDateTime.to_i
-
@orig = @timestamp
-
end
-
end
-
-
# Returns the time as a Time.
-
1
def to_time
-
unless @time
-
if @timestamp
-
@time = Time.at(@timestamp).utc
-
else
-
@time = Time.utc(year, mon, mday, hour, min, sec)
-
end
-
end
-
-
@time
-
end
-
-
# Returns the time as a DateTime.
-
1
def to_datetime
-
unless @datetime
-
@datetime = DateTime.new(year, mon, mday, hour, min, sec)
-
end
-
-
@datetime
-
end
-
-
# Returns the time as an integer timestamp.
-
1
def to_i
-
unless @timestamp
-
@timestamp = to_time.to_i
-
end
-
-
@timestamp
-
end
-
-
# Returns the time as the original time passed to new.
-
1
def to_orig
-
@orig
-
end
-
-
# Returns a string representation of the TimeOrDateTime.
-
1
def to_s
-
if @orig.is_a?(Time)
-
"Time: #{@orig.to_s}"
-
elsif @orig.is_a?(DateTime)
-
"DateTime: #{@orig.to_s}"
-
else
-
"Timestamp: #{@orig.to_s}"
-
end
-
end
-
-
# Returns internal object state as a programmer-readable string.
-
1
def inspect
-
"#<#{self.class}: #{@orig.inspect}>"
-
end
-
-
# Returns the year.
-
1
def year
-
if @time
-
@time.year
-
elsif @datetime
-
@datetime.year
-
else
-
to_time.year
-
end
-
end
-
-
# Returns the month of the year (1..12).
-
1
def mon
-
if @time
-
@time.mon
-
elsif @datetime
-
@datetime.mon
-
else
-
to_time.mon
-
end
-
end
-
1
alias :month :mon
-
-
# Returns the day of the month (1..n).
-
1
def mday
-
if @time
-
@time.mday
-
elsif @datetime
-
@datetime.mday
-
else
-
to_time.mday
-
end
-
end
-
1
alias :day :mday
-
-
# Returns the hour of the day (0..23).
-
1
def hour
-
if @time
-
@time.hour
-
elsif @datetime
-
@datetime.hour
-
else
-
to_time.hour
-
end
-
end
-
-
# Returns the minute of the hour (0..59).
-
1
def min
-
if @time
-
@time.min
-
elsif @datetime
-
@datetime.min
-
else
-
to_time.min
-
end
-
end
-
-
# Returns the second of the minute (0..60). (60 for a leap second).
-
1
def sec
-
if @time
-
@time.sec
-
elsif @datetime
-
@datetime.sec
-
else
-
to_time.sec
-
end
-
end
-
-
# Compares this TimeOrDateTime with another Time, DateTime, integer
-
# timestamp or TimeOrDateTime. Returns -1, 0 or +1 depending whether the
-
# receiver is less than, equal to, or greater than timeOrDateTime.
-
#
-
# Milliseconds and smaller units are ignored in the comparison.
-
1
def <=>(timeOrDateTime)
-
if timeOrDateTime.is_a?(TimeOrDateTime)
-
orig = timeOrDateTime.to_orig
-
-
if @orig.is_a?(DateTime) || orig.is_a?(DateTime)
-
# If either is a DateTime, assume it is there for a reason
-
# (i.e. for range).
-
to_datetime <=> timeOrDateTime.to_datetime
-
elsif orig.is_a?(Time)
-
to_time <=> timeOrDateTime.to_time
-
else
-
to_i <=> timeOrDateTime.to_i
-
end
-
elsif @orig.is_a?(DateTime) || timeOrDateTime.is_a?(DateTime)
-
# If either is a DateTime, assume it is there for a reason
-
# (i.e. for range).
-
to_datetime <=> TimeOrDateTime.wrap(timeOrDateTime).to_datetime
-
elsif timeOrDateTime.is_a?(Time)
-
to_time <=> timeOrDateTime
-
else
-
to_i <=> timeOrDateTime.to_i
-
end
-
end
-
-
# Adds a number of seconds to the TimeOrDateTime. Returns a new
-
# TimeOrDateTime, preserving what the original constructed type was.
-
# If the original type is a Time and the resulting calculation goes out of
-
# range for Times, then an exception will be raised by the Time class.
-
1
def +(seconds)
-
if seconds == 0
-
self
-
else
-
if @orig.is_a?(DateTime)
-
TimeOrDateTime.new(@orig + OffsetRationals.rational_for_offset(seconds))
-
else
-
# + defined for Time and integer timestamps
-
TimeOrDateTime.new(@orig + seconds)
-
end
-
end
-
end
-
-
# Subtracts a number of seconds from the TimeOrDateTime. Returns a new
-
# TimeOrDateTime, preserving what the original constructed type was.
-
# If the original type is a Time and the resulting calculation goes out of
-
# range for Times, then an exception will be raised by the Time class.
-
1
def -(seconds)
-
self + (-seconds)
-
end
-
-
# Similar to the + operator, but for cases where adding would cause a
-
# timestamp or time to go out of the allowed range, converts to a DateTime
-
# based TimeOrDateTime.
-
1
def add_with_convert(seconds)
-
if seconds == 0
-
self
-
else
-
if @orig.is_a?(DateTime)
-
TimeOrDateTime.new(@orig + OffsetRationals.rational_for_offset(seconds))
-
else
-
# A Time or timestamp.
-
result = to_i + seconds
-
-
if result < 0 || result > 2147483647
-
result = TimeOrDateTime.new(to_datetime + OffsetRationals.rational_for_offset(seconds))
-
else
-
result = TimeOrDateTime.new(@orig + seconds)
-
end
-
end
-
end
-
end
-
-
# Returns true if todt represents the same time and was originally
-
# constructed with the same type (DateTime, Time or timestamp) as this
-
# TimeOrDateTime.
-
1
def eql?(todt)
-
todt.respond_to?(:to_orig) && to_orig.eql?(todt.to_orig)
-
end
-
-
# Returns a hash of this TimeOrDateTime.
-
1
def hash
-
@orig.hash
-
end
-
-
# If no block is given, returns a TimeOrDateTime wrapping the given
-
# timeOrDateTime. If a block is specified, a TimeOrDateTime is constructed
-
# and passed to the block. The result of the block must be a TimeOrDateTime.
-
# to_orig will be called on the result and the result of to_orig will be
-
# returned.
-
#
-
# timeOrDateTime can be a Time, DateTime, integer timestamp or TimeOrDateTime.
-
# If a TimeOrDateTime is passed in, no new TimeOrDateTime will be constructed,
-
# the passed in value will be used.
-
1
def self.wrap(timeOrDateTime)
-
t = timeOrDateTime.is_a?(TimeOrDateTime) ? timeOrDateTime : TimeOrDateTime.new(timeOrDateTime)
-
-
if block_given?
-
t = yield t
-
-
if timeOrDateTime.is_a?(TimeOrDateTime)
-
t
-
elsif timeOrDateTime.is_a?(Time)
-
t.to_time
-
elsif timeOrDateTime.is_a?(DateTime)
-
t.to_datetime
-
else
-
t.to_i
-
end
-
else
-
t
-
end
-
end
-
end
-
end
-
#--
-
# Copyright (c) 2005-2010 Philip Ross
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
-
# of this software and associated documentation files (the "Software"), to deal
-
# in the Software without restriction, including without limitation the rights
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-
# copies of the Software, and to permit persons to whom the Software is
-
# furnished to do so, subject to the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be included in all
-
# copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-
# THE SOFTWARE.
-
#++
-
-
1
require 'date'
-
-
1
module TZInfo
-
# Indicate a specified time in a local timezone has more than one
-
# possible time in UTC. This happens when switching from daylight savings time
-
# to normal time where the clocks are rolled back. Thrown by period_for_local
-
# and local_to_utc when using an ambiguous time and not specifying any
-
# means to resolve the ambiguity.
-
1
class AmbiguousTime < StandardError
-
end
-
-
# Thrown to indicate that no TimezonePeriod matching a given time could be found.
-
1
class PeriodNotFound < StandardError
-
end
-
-
# Thrown by Timezone#get if the identifier given is not valid.
-
1
class InvalidTimezoneIdentifier < StandardError
-
end
-
-
# Thrown if an attempt is made to use a timezone created with Timezone.new(nil).
-
1
class UnknownTimezone < StandardError
-
end
-
-
# Timezone is the base class of all timezones. It provides a factory method
-
# get to access timezones by identifier. Once a specific Timezone has been
-
# retrieved, DateTimes, Times and timestamps can be converted between the UTC
-
# and the local time for the zone. For example:
-
#
-
# tz = TZInfo::Timezone.get('America/New_York')
-
# puts tz.utc_to_local(DateTime.new(2005,8,29,15,35,0)).to_s
-
# puts tz.local_to_utc(Time.utc(2005,8,29,11,35,0)).to_s
-
# puts tz.utc_to_local(1125315300).to_s
-
#
-
# Each time conversion method returns an object of the same type it was
-
# passed.
-
#
-
# The timezone information all comes from the tz database
-
# (see http://www.twinsun.com/tz/tz-link.htm)
-
1
class Timezone
-
1
include Comparable
-
-
# Cache of loaded zones by identifier to avoid using require if a zone
-
# has already been loaded.
-
1
@@loaded_zones = {}
-
-
# Whether the timezones index has been loaded yet.
-
1
@@index_loaded = false
-
-
# Default value of the dst parameter of the local_to_utc and
-
# period_for_local methods.
-
1
@@default_dst = nil
-
-
# Sets the default value of the optional dst parameter of the
-
# local_to_utc and period_for_local methods. Can be set to nil, true or
-
# false.
-
#
-
# The value of default_dst defaults to nil if unset.
-
1
def self.default_dst=(value)
-
@@default_dst = value.nil? ? nil : !!value
-
end
-
-
# Gets the default value of the optional dst parameter of the
-
# local_to_utc and period_for_local methods. Can be set to nil, true or
-
# false.
-
1
def self.default_dst
-
@@default_dst
-
end
-
-
# Returns a timezone by its identifier (e.g. "Europe/London",
-
# "America/Chicago" or "UTC").
-
#
-
# Raises InvalidTimezoneIdentifier if the timezone couldn't be found.
-
1
def self.get(identifier)
-
instance = @@loaded_zones[identifier]
-
unless instance
-
raise InvalidTimezoneIdentifier, 'Invalid identifier' if identifier !~ /^[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*$/
-
identifier = identifier.gsub(/-/, '__m__').gsub(/\+/, '__p__')
-
begin
-
# Use a temporary variable to avoid an rdoc warning
-
file = "tzinfo/definitions/#{identifier}".untaint
-
require file
-
-
m = Definitions
-
identifier.split(/\//).each {|part|
-
m = m.const_get(part)
-
}
-
-
info = m.get
-
-
# Could make Timezone subclasses register an interest in an info
-
# type. Since there are currently only two however, there isn't
-
# much point.
-
if info.kind_of?(DataTimezoneInfo)
-
instance = DataTimezone.new(info)
-
elsif info.kind_of?(LinkedTimezoneInfo)
-
instance = LinkedTimezone.new(info)
-
else
-
raise InvalidTimezoneIdentifier, "No handler for info type #{info.class}"
-
end
-
-
@@loaded_zones[instance.identifier] = instance
-
rescue LoadError, NameError => e
-
raise InvalidTimezoneIdentifier, e.message
-
end
-
end
-
-
instance
-
end
-
-
# Returns a proxy for the Timezone with the given identifier. The proxy
-
# will cause the real timezone to be loaded when an attempt is made to
-
# find a period or convert a time. get_proxy will not validate the
-
# identifier. If an invalid identifier is specified, no exception will be
-
# raised until the proxy is used.
-
1
def self.get_proxy(identifier)
-
TimezoneProxy.new(identifier)
-
end
-
-
# If identifier is nil calls super(), otherwise calls get. An identfier
-
# should always be passed in when called externally.
-
1
def self.new(identifier = nil)
-
1
if identifier
-
get(identifier)
-
else
-
1
super()
-
end
-
end
-
-
# Returns an array containing all the available Timezones.
-
#
-
# Returns TimezoneProxy objects to avoid the overhead of loading Timezone
-
# definitions until a conversion is actually required.
-
1
def self.all
-
get_proxies(all_identifiers)
-
end
-
-
# Returns an array containing the identifiers of all the available
-
# Timezones.
-
1
def self.all_identifiers
-
load_index
-
Indexes::Timezones.timezones
-
end
-
-
# Returns an array containing all the available Timezones that are based
-
# on data (are not links to other Timezones).
-
#
-
# Returns TimezoneProxy objects to avoid the overhead of loading Timezone
-
# definitions until a conversion is actually required.
-
1
def self.all_data_zones
-
get_proxies(all_data_zone_identifiers)
-
end
-
-
# Returns an array containing the identifiers of all the available
-
# Timezones that are based on data (are not links to other Timezones)..
-
1
def self.all_data_zone_identifiers
-
load_index
-
Indexes::Timezones.data_timezones
-
end
-
-
# Returns an array containing all the available Timezones that are links
-
# to other Timezones.
-
#
-
# Returns TimezoneProxy objects to avoid the overhead of loading Timezone
-
# definitions until a conversion is actually required.
-
1
def self.all_linked_zones
-
get_proxies(all_linked_zone_identifiers)
-
end
-
-
# Returns an array containing the identifiers of all the available
-
# Timezones that are links to other Timezones.
-
1
def self.all_linked_zone_identifiers
-
load_index
-
Indexes::Timezones.linked_timezones
-
end
-
-
# Returns all the Timezones defined for all Countries. This is not the
-
# complete set of Timezones as some are not country specific (e.g.
-
# 'Etc/GMT').
-
#
-
# Returns TimezoneProxy objects to avoid the overhead of loading Timezone
-
# definitions until a conversion is actually required.
-
1
def self.all_country_zones
-
Country.all_codes.inject([]) {|zones,country|
-
zones += Country.get(country).zones
-
}
-
end
-
-
# Returns all the zone identifiers defined for all Countries. This is not the
-
# complete set of zone identifiers as some are not country specific (e.g.
-
# 'Etc/GMT'). You can obtain a Timezone instance for a given identifier
-
# with the get method.
-
1
def self.all_country_zone_identifiers
-
Country.all_codes.inject([]) {|zones,country|
-
zones += Country.get(country).zone_identifiers
-
}
-
end
-
-
# Returns all US Timezone instances. A shortcut for
-
# TZInfo::Country.get('US').zones.
-
#
-
# Returns TimezoneProxy objects to avoid the overhead of loading Timezone
-
# definitions until a conversion is actually required.
-
1
def self.us_zones
-
Country.get('US').zones
-
end
-
-
# Returns all US zone identifiers. A shortcut for
-
# TZInfo::Country.get('US').zone_identifiers.
-
1
def self.us_zone_identifiers
-
Country.get('US').zone_identifiers
-
end
-
-
# The identifier of the timezone, e.g. "Europe/Paris".
-
1
def identifier
-
raise UnknownTimezone, 'TZInfo::Timezone constructed directly'
-
end
-
-
# An alias for identifier.
-
1
def name
-
# Don't use alias, as identifier gets overridden.
-
identifier
-
end
-
-
# Returns a friendlier version of the identifier.
-
1
def to_s
-
friendly_identifier
-
end
-
-
# Returns internal object state as a programmer-readable string.
-
1
def inspect
-
"#<#{self.class}: #{identifier}>"
-
end
-
-
# Returns a friendlier version of the identifier. Set skip_first_part to
-
# omit the first part of the identifier (typically a region name) where
-
# there is more than one part.
-
#
-
# For example:
-
#
-
# Timezone.get('Europe/Paris').friendly_identifier(false) #=> "Europe - Paris"
-
# Timezone.get('Europe/Paris').friendly_identifier(true) #=> "Paris"
-
# Timezone.get('America/Indiana/Knox').friendly_identifier(false) #=> "America - Knox, Indiana"
-
# Timezone.get('America/Indiana/Knox').friendly_identifier(true) #=> "Knox, Indiana"
-
1
def friendly_identifier(skip_first_part = false)
-
parts = identifier.split('/')
-
if parts.empty?
-
# shouldn't happen
-
identifier
-
elsif parts.length == 1
-
parts[0]
-
else
-
if skip_first_part
-
result = ''
-
else
-
result = parts[0] + ' - '
-
end
-
-
parts[1, parts.length - 1].reverse_each {|part|
-
part.gsub!(/_/, ' ')
-
-
if part.index(/[a-z]/)
-
# Missing a space if a lower case followed by an upper case and the
-
# name isn't McXxxx.
-
part.gsub!(/([^M][a-z])([A-Z])/, '\1 \2')
-
part.gsub!(/([M][a-bd-z])([A-Z])/, '\1 \2')
-
-
# Missing an apostrophe if two consecutive upper case characters.
-
part.gsub!(/([A-Z])([A-Z])/, '\1\'\2')
-
end
-
-
result << part
-
result << ', '
-
}
-
-
result.slice!(result.length - 2, 2)
-
result
-
end
-
end
-
-
# Returns the TimezonePeriod for the given UTC time. utc can either be
-
# a DateTime, Time or integer timestamp (Time.to_i). Any timezone
-
# information in utc is ignored (it is treated as a UTC time).
-
1
def period_for_utc(utc)
-
raise UnknownTimezone, 'TZInfo::Timezone constructed directly'
-
end
-
-
# Returns the set of TimezonePeriod instances that are valid for the given
-
# local time as an array. If you just want a single period, use
-
# period_for_local instead and specify how ambiguities should be resolved.
-
# Returns an empty array if no periods are found for the given time.
-
1
def periods_for_local(local)
-
raise UnknownTimezone, 'TZInfo::Timezone constructed directly'
-
end
-
-
# Returns the TimezonePeriod for the given local time. local can either be
-
# a DateTime, Time or integer timestamp (Time.to_i). Any timezone
-
# information in local is ignored (it is treated as a time in the current
-
# timezone).
-
#
-
# Warning: There are local times that have no equivalent UTC times (e.g.
-
# in the transition from standard time to daylight savings time). There are
-
# also local times that have more than one UTC equivalent (e.g. in the
-
# transition from daylight savings time to standard time).
-
#
-
# In the first case (no equivalent UTC time), a PeriodNotFound exception
-
# will be raised.
-
#
-
# In the second case (more than one equivalent UTC time), an AmbiguousTime
-
# exception will be raised unless the optional dst parameter or block
-
# handles the ambiguity.
-
#
-
# If the ambiguity is due to a transition from daylight savings time to
-
# standard time, the dst parameter can be used to select whether the
-
# daylight savings time or local time is used. For example,
-
#
-
# Timezone.get('America/New_York').period_for_local(DateTime.new(2004,10,31,1,30,0))
-
#
-
# would raise an AmbiguousTime exception.
-
#
-
# Specifying dst=true would the daylight savings period from April to
-
# October 2004. Specifying dst=false would return the standard period
-
# from October 2004 to April 2005.
-
#
-
# If the dst parameter does not resolve the ambiguity, and a block is
-
# specified, it is called. The block must take a single parameter - an
-
# array of the periods that need to be resolved. The block can select and
-
# return a single period or return nil or an empty array
-
# to cause an AmbiguousTime exception to be raised.
-
#
-
# The default value of the dst parameter can be specified by setting
-
# Timezone.default_dst. If default_dst is not set, or is set to nil, then
-
# an AmbiguousTime exception will be raised in ambiguous situations unless
-
# a block is given to resolve the ambiguity.
-
1
def period_for_local(local, dst = Timezone.default_dst)
-
results = periods_for_local(local)
-
-
if results.empty?
-
raise PeriodNotFound
-
elsif results.size < 2
-
results.first
-
else
-
# ambiguous result try to resolve
-
-
if !dst.nil?
-
matches = results.find_all {|period| period.dst? == dst}
-
results = matches if !matches.empty?
-
end
-
-
if results.size < 2
-
results.first
-
else
-
# still ambiguous, try the block
-
-
if block_given?
-
results = yield results
-
end
-
-
if results.is_a?(TimezonePeriod)
-
results
-
elsif results && results.size == 1
-
results.first
-
else
-
raise AmbiguousTime, "#{local} is an ambiguous local time."
-
end
-
end
-
end
-
end
-
-
# Converts a time in UTC to the local timezone. utc can either be
-
# a DateTime, Time or timestamp (Time.to_i). The returned time has the same
-
# type as utc. Any timezone information in utc is ignored (it is treated as
-
# a UTC time).
-
1
def utc_to_local(utc)
-
TimeOrDateTime.wrap(utc) {|wrapped|
-
period_for_utc(wrapped).to_local(wrapped)
-
}
-
end
-
-
# Converts a time in the local timezone to UTC. local can either be
-
# a DateTime, Time or timestamp (Time.to_i). The returned time has the same
-
# type as local. Any timezone information in local is ignored (it is treated
-
# as a local time).
-
#
-
# Warning: There are local times that have no equivalent UTC times (e.g.
-
# in the transition from standard time to daylight savings time). There are
-
# also local times that have more than one UTC equivalent (e.g. in the
-
# transition from daylight savings time to standard time).
-
#
-
# In the first case (no equivalent UTC time), a PeriodNotFound exception
-
# will be raised.
-
#
-
# In the second case (more than one equivalent UTC time), an AmbiguousTime
-
# exception will be raised unless the optional dst parameter or block
-
# handles the ambiguity.
-
#
-
# If the ambiguity is due to a transition from daylight savings time to
-
# standard time, the dst parameter can be used to select whether the
-
# daylight savings time or local time is used. For example,
-
#
-
# Timezone.get('America/New_York').local_to_utc(DateTime.new(2004,10,31,1,30,0))
-
#
-
# would raise an AmbiguousTime exception.
-
#
-
# Specifying dst=true would return 2004-10-31 5:30:00. Specifying dst=false
-
# would return 2004-10-31 6:30:00.
-
#
-
# If the dst parameter does not resolve the ambiguity, and a block is
-
# specified, it is called. The block must take a single parameter - an
-
# array of the periods that need to be resolved. The block can return a
-
# single period to use to convert the time or return nil or an empty array
-
# to cause an AmbiguousTime exception to be raised.
-
#
-
# The default value of the dst parameter can be specified by setting
-
# Timezone.default_dst. If default_dst is not set, or is set to nil, then
-
# an AmbiguousTime exception will be raised in ambiguous situations unless
-
# a block is given to resolve the ambiguity.
-
1
def local_to_utc(local, dst = Timezone.default_dst)
-
TimeOrDateTime.wrap(local) {|wrapped|
-
if block_given?
-
period = period_for_local(wrapped, dst) {|periods| yield periods }
-
else
-
period = period_for_local(wrapped, dst)
-
end
-
-
period.to_utc(wrapped)
-
}
-
end
-
-
# Returns the current time in the timezone as a Time.
-
1
def now
-
utc_to_local(Time.now.utc)
-
end
-
-
# Returns the TimezonePeriod for the current time.
-
1
def current_period
-
period_for_utc(Time.now.utc)
-
end
-
-
# Returns the current Time and TimezonePeriod as an array. The first element
-
# is the time, the second element is the period.
-
1
def current_period_and_time
-
utc = Time.now.utc
-
period = period_for_utc(utc)
-
[period.to_local(utc), period]
-
end
-
-
1
alias :current_time_and_period :current_period_and_time
-
-
# Converts a time in UTC to local time and returns it as a string
-
# according to the given format. The formatting is identical to
-
# Time.strftime and DateTime.strftime, except %Z is replaced with the
-
# timezone abbreviation for the specified time (for example, EST or EDT).
-
1
def strftime(format, utc = Time.now.utc)
-
period = period_for_utc(utc)
-
local = period.to_local(utc)
-
local = Time.at(local).utc unless local.kind_of?(Time) || local.kind_of?(DateTime)
-
abbreviation = period.abbreviation.to_s.gsub(/%/, '%%')
-
-
format = format.gsub(/(.?)%Z/) do
-
if $1 == '%'
-
# return %%Z so the real strftime treats it as a literal %Z too
-
'%%Z'
-
else
-
"#$1#{abbreviation}"
-
end
-
end
-
-
local.strftime(format)
-
end
-
-
# Compares two Timezones based on their identifier. Returns -1 if tz is less
-
# than self, 0 if tz is equal to self and +1 if tz is greater than self.
-
1
def <=>(tz)
-
identifier <=> tz.identifier
-
end
-
-
# Returns true if and only if the identifier of tz is equal to the
-
# identifier of this Timezone.
-
1
def eql?(tz)
-
self == tz
-
end
-
-
# Returns a hash of this Timezone.
-
1
def hash
-
identifier.hash
-
end
-
-
# Dumps this Timezone for marshalling.
-
1
def _dump(limit)
-
identifier
-
end
-
-
# Loads a marshalled Timezone.
-
1
def self._load(data)
-
Timezone.get(data)
-
end
-
-
1
private
-
# Loads in the index of timezones if it hasn't already been loaded.
-
1
def self.load_index
-
unless @@index_loaded
-
require 'tzinfo/indexes/timezones'
-
@@index_loaded = true
-
end
-
end
-
-
# Returns an array of proxies corresponding to the given array of
-
# identifiers.
-
1
def self.get_proxies(identifiers)
-
identifiers.collect {|identifier| get_proxy(identifier)}
-
end
-
end
-
end
-
#--
-
# Copyright (c) 2006-2010 Philip Ross
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
-
# of this software and associated documentation files (the "Software"), to deal
-
# in the Software without restriction, including without limitation the rights
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-
# copies of the Software, and to permit persons to whom the Software is
-
# furnished to do so, subject to the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be included in all
-
# copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-
# THE SOFTWARE.
-
#++
-
-
1
module TZInfo
-
-
# TimezoneDefinition is included into Timezone definition modules.
-
# TimezoneDefinition provides the methods for defining timezones.
-
1
module TimezoneDefinition #:nodoc:
-
# Add class methods to the includee.
-
1
def self.append_features(base)
-
super
-
base.extend(ClassMethods)
-
end
-
-
# Class methods for inclusion.
-
1
module ClassMethods #:nodoc:
-
# Returns and yields a DataTimezoneInfo object to define a timezone.
-
1
def timezone(identifier)
-
yield @timezone = DataTimezoneInfo.new(identifier)
-
end
-
-
# Defines a linked timezone.
-
1
def linked_timezone(identifier, link_to_identifier)
-
@timezone = LinkedTimezoneInfo.new(identifier, link_to_identifier)
-
end
-
-
# Returns the last TimezoneInfo to be defined with timezone or
-
# linked_timezone.
-
1
def get
-
@timezone
-
end
-
end
-
end
-
end
-
#--
-
# Copyright (c) 2006 Philip Ross
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
-
# of this software and associated documentation files (the "Software"), to deal
-
# in the Software without restriction, including without limitation the rights
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-
# copies of the Software, and to permit persons to whom the Software is
-
# furnished to do so, subject to the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be included in all
-
# copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-
# THE SOFTWARE.
-
#++
-
-
1
module TZInfo
-
# The timezone index file includes TimezoneIndexDefinition which provides
-
# methods used to define timezones in the index.
-
1
module TimezoneIndexDefinition #:nodoc:
-
1
def self.append_features(base)
-
super
-
base.extend(ClassMethods)
-
base.instance_eval do
-
@timezones = []
-
@data_timezones = []
-
@linked_timezones = []
-
end
-
end
-
-
1
module ClassMethods #:nodoc:
-
# Defines a timezone based on data.
-
1
def timezone(identifier)
-
@timezones << identifier
-
@data_timezones << identifier
-
end
-
-
# Defines a timezone which is a link to another timezone.
-
1
def linked_timezone(identifier)
-
@timezones << identifier
-
@linked_timezones << identifier
-
end
-
-
# Returns a frozen array containing the identifiers of all the timezones.
-
# Identifiers appear in the order they were defined in the index.
-
1
def timezones
-
@timezones.freeze
-
end
-
-
# Returns a frozen array containing the identifiers of all data timezones.
-
# Identifiers appear in the order they were defined in the index.
-
1
def data_timezones
-
@data_timezones.freeze
-
end
-
-
# Returns a frozen array containing the identifiers of all linked
-
# timezones. Identifiers appear in the order they were defined in
-
# the index.
-
1
def linked_timezones
-
@linked_timezones.freeze
-
end
-
end
-
end
-
end
-
#--
-
# Copyright (c) 2006 Philip Ross
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
-
# of this software and associated documentation files (the "Software"), to deal
-
# in the Software without restriction, including without limitation the rights
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-
# copies of the Software, and to permit persons to whom the Software is
-
# furnished to do so, subject to the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be included in all
-
# copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-
# THE SOFTWARE.
-
#++
-
-
1
module TZInfo
-
# Represents a timezone defined in a data module.
-
1
class TimezoneInfo #:nodoc:
-
-
# The timezone identifier.
-
1
attr_reader :identifier
-
-
# Constructs a new TimezoneInfo with an identifier.
-
1
def initialize(identifier)
-
@identifier = identifier
-
end
-
-
# Returns internal object state as a programmer-readable string.
-
1
def inspect
-
"#<#{self.class}: #@identifier>"
-
end
-
end
-
end
-
#--
-
# Copyright (c) 2006 Philip Ross
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
-
# of this software and associated documentation files (the "Software"), to deal
-
# in the Software without restriction, including without limitation the rights
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-
# copies of the Software, and to permit persons to whom the Software is
-
# furnished to do so, subject to the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be included in all
-
# copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-
# THE SOFTWARE.
-
#++
-
-
1
module TZInfo
-
# Represents an offset defined in a Timezone data file.
-
1
class TimezoneOffsetInfo #:nodoc:
-
# The base offset of the timezone from UTC in seconds.
-
1
attr_reader :utc_offset
-
-
# The offset from standard time for the zone in seconds (i.e. non-zero if
-
# daylight savings is being observed).
-
1
attr_reader :std_offset
-
-
# The total offset of this observance from UTC in seconds
-
# (utc_offset + std_offset).
-
1
attr_reader :utc_total_offset
-
-
# The abbreviation that identifies this observance, e.g. "GMT"
-
# (Greenwich Mean Time) or "BST" (British Summer Time) for "Europe/London". The returned identifier is a
-
# symbol.
-
1
attr_reader :abbreviation
-
-
# Constructs a new TimezoneOffsetInfo. utc_offset and std_offset are
-
# specified in seconds.
-
1
def initialize(utc_offset, std_offset, abbreviation)
-
@utc_offset = utc_offset
-
@std_offset = std_offset
-
@abbreviation = abbreviation
-
-
@utc_total_offset = @utc_offset + @std_offset
-
end
-
-
# True if std_offset is non-zero.
-
1
def dst?
-
@std_offset != 0
-
end
-
-
# Converts a UTC DateTime to local time based on the offset of this period.
-
1
def to_local(utc)
-
TimeOrDateTime.wrap(utc) {|wrapped|
-
wrapped + @utc_total_offset
-
}
-
end
-
-
# Converts a local DateTime to UTC based on the offset of this period.
-
1
def to_utc(local)
-
TimeOrDateTime.wrap(local) {|wrapped|
-
wrapped - @utc_total_offset
-
}
-
end
-
-
# Returns true if and only if toi has the same utc_offset, std_offset
-
# and abbreviation as this TimezoneOffsetInfo.
-
1
def ==(toi)
-
toi.respond_to?(:utc_offset) && toi.respond_to?(:std_offset) && toi.respond_to?(:abbreviation) &&
-
utc_offset == toi.utc_offset && std_offset == toi.std_offset && abbreviation == toi.abbreviation
-
end
-
-
# Returns true if and only if toi has the same utc_offset, std_offset
-
# and abbreviation as this TimezoneOffsetInfo.
-
1
def eql?(toi)
-
self == toi
-
end
-
-
# Returns a hash of this TimezoneOffsetInfo.
-
1
def hash
-
utc_offset.hash ^ std_offset.hash ^ abbreviation.hash
-
end
-
-
# Returns internal object state as a programmer-readable string.
-
1
def inspect
-
"#<#{self.class}: #@utc_offset,#@std_offset,#@abbreviation>"
-
end
-
end
-
end
-
#--
-
# Copyright (c) 2005-2010 Philip Ross
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
-
# of this software and associated documentation files (the "Software"), to deal
-
# in the Software without restriction, including without limitation the rights
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-
# copies of the Software, and to permit persons to whom the Software is
-
# furnished to do so, subject to the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be included in all
-
# copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-
# THE SOFTWARE.
-
#++
-
-
1
module TZInfo
-
# A period of time in a timezone where the same offset from UTC applies.
-
#
-
# All the methods that take times accept instances of Time, DateTime or
-
# integer timestamps.
-
1
class TimezonePeriod
-
# The TimezoneTransitionInfo that defines the start of this TimezonePeriod
-
# (may be nil if unbounded).
-
1
attr_reader :start_transition
-
-
# The TimezoneTransitionInfo that defines the end of this TimezonePeriod
-
# (may be nil if unbounded).
-
1
attr_reader :end_transition
-
-
# The TimezoneOffsetInfo for this period.
-
1
attr_reader :offset
-
-
# Initializes a new TimezonePeriod.
-
1
def initialize(start_transition, end_transition, offset = nil)
-
@start_transition = start_transition
-
@end_transition = end_transition
-
-
if offset
-
raise ArgumentError, 'Offset specified with transitions' if @start_transition || @end_transition
-
@offset = offset
-
else
-
if @start_transition
-
@offset = @start_transition.offset
-
elsif @end_transition
-
@offset = @end_transition.previous_offset
-
else
-
raise ArgumentError, 'No offset specified and no transitions to determine it from'
-
end
-
end
-
-
@utc_total_offset_rational = nil
-
end
-
-
# Base offset of the timezone from UTC (seconds).
-
1
def utc_offset
-
@offset.utc_offset
-
end
-
-
# Offset from the local time where daylight savings is in effect (seconds).
-
# E.g.: utc_offset could be -5 hours. Normally, std_offset would be 0.
-
# During daylight savings, std_offset would typically become +1 hours.
-
1
def std_offset
-
@offset.std_offset
-
end
-
-
# The identifier of this period, e.g. "GMT" (Greenwich Mean Time) or "BST"
-
# (British Summer Time) for "Europe/London". The returned identifier is a
-
# symbol.
-
1
def abbreviation
-
@offset.abbreviation
-
end
-
1
alias :zone_identifier :abbreviation
-
-
# Total offset from UTC (seconds). Equal to utc_offset + std_offset.
-
1
def utc_total_offset
-
@offset.utc_total_offset
-
end
-
-
# Total offset from UTC (days). Result is a Rational.
-
1
def utc_total_offset_rational
-
unless @utc_total_offset_rational
-
@utc_total_offset_rational = OffsetRationals.rational_for_offset(utc_total_offset)
-
end
-
@utc_total_offset_rational
-
end
-
-
# The start time of the period in UTC as a DateTime. May be nil if unbounded.
-
1
def utc_start
-
@start_transition ? @start_transition.at.to_datetime : nil
-
end
-
-
# The end time of the period in UTC as a DateTime. May be nil if unbounded.
-
1
def utc_end
-
@end_transition ? @end_transition.at.to_datetime : nil
-
end
-
-
# The start time of the period in local time as a DateTime. May be nil if
-
# unbounded.
-
1
def local_start
-
@start_transition ? @start_transition.local_start.to_datetime : nil
-
end
-
-
# The end time of the period in local time as a DateTime. May be nil if
-
# unbounded.
-
1
def local_end
-
@end_transition ? @end_transition.local_end.to_datetime : nil
-
end
-
-
# true if daylight savings is in effect for this period; otherwise false.
-
1
def dst?
-
@offset.dst?
-
end
-
-
# true if this period is valid for the given UTC DateTime; otherwise false.
-
1
def valid_for_utc?(utc)
-
utc_after_start?(utc) && utc_before_end?(utc)
-
end
-
-
# true if the given UTC DateTime is after the start of the period
-
# (inclusive); otherwise false.
-
1
def utc_after_start?(utc)
-
!@start_transition || @start_transition.at <= utc
-
end
-
-
# true if the given UTC DateTime is before the end of the period
-
# (exclusive); otherwise false.
-
1
def utc_before_end?(utc)
-
!@end_transition || @end_transition.at > utc
-
end
-
-
# true if this period is valid for the given local DateTime; otherwise false.
-
1
def valid_for_local?(local)
-
local_after_start?(local) && local_before_end?(local)
-
end
-
-
# true if the given local DateTime is after the start of the period
-
# (inclusive); otherwise false.
-
1
def local_after_start?(local)
-
!@start_transition || @start_transition.local_start <= local
-
end
-
-
# true if the given local DateTime is before the end of the period
-
# (exclusive); otherwise false.
-
1
def local_before_end?(local)
-
!@end_transition || @end_transition.local_end > local
-
end
-
-
# Converts a UTC DateTime to local time based on the offset of this period.
-
1
def to_local(utc)
-
@offset.to_local(utc)
-
end
-
-
# Converts a local DateTime to UTC based on the offset of this period.
-
1
def to_utc(local)
-
@offset.to_utc(local)
-
end
-
-
# Returns true if this TimezonePeriod is equal to p. This compares the
-
# start_transition, end_transition and offset using ==.
-
1
def ==(p)
-
p.respond_to?(:start_transition) && p.respond_to?(:end_transition) &&
-
p.respond_to?(:offset) && start_transition == p.start_transition &&
-
end_transition == p.end_transition && offset == p.offset
-
end
-
-
# Returns true if this TimezonePeriods is equal to p. This compares the
-
# start_transition, end_transition and offset using eql?
-
1
def eql?(p)
-
p.respond_to?(:start_transition) && p.respond_to?(:end_transition) &&
-
p.respond_to?(:offset) && start_transition.eql?(p.start_transition) &&
-
end_transition.eql?(p.end_transition) && offset.eql?(p.offset)
-
end
-
-
# Returns a hash of this TimezonePeriod.
-
1
def hash
-
result = @start_transition.hash ^ @end_transition.hash
-
result ^= @offset.hash unless @start_transition || @end_transition
-
result
-
end
-
-
# Returns internal object state as a programmer-readable string.
-
1
def inspect
-
result = "#<#{self.class}: #{@start_transition.inspect},#{@end_transition.inspect}"
-
result << ",#{@offset.inspect}>" unless @start_transition || @end_transition
-
result + '>'
-
end
-
end
-
end
-
#--
-
# Copyright (c) 2005-2010 Philip Ross
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
-
# of this software and associated documentation files (the "Software"), to deal
-
# in the Software without restriction, including without limitation the rights
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-
# copies of the Software, and to permit persons to whom the Software is
-
# furnished to do so, subject to the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be included in all
-
# copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-
# THE SOFTWARE.
-
#++
-
-
1
module TZInfo
-
-
# A proxy class representing a timezone with a given identifier. TimezoneProxy
-
# inherits from Timezone and can be treated like any Timezone loaded with
-
# Timezone.get.
-
#
-
# The first time an attempt is made to access the data for the timezone, the
-
# real Timezone is loaded. If the proxy's identifier was not valid, then an
-
# exception will be raised at this point.
-
1
class TimezoneProxy < Timezone
-
# Construct a new TimezoneProxy for the given identifier. The identifier
-
# is not checked when constructing the proxy. It will be validated on the
-
# when the real Timezone is loaded.
-
1
def self.new(identifier)
-
# Need to override new to undo the behaviour introduced in Timezone#new.
-
1
tzp = super()
-
1
tzp.send(:setup, identifier)
-
1
tzp
-
end
-
-
# The identifier of the timezone, e.g. "Europe/Paris".
-
1
def identifier
-
@real_timezone ? @real_timezone.identifier : @identifier
-
end
-
-
# Returns the TimezonePeriod for the given UTC time. utc can either be
-
# a DateTime, Time or integer timestamp (Time.to_i). Any timezone
-
# information in utc is ignored (it is treated as a UTC time).
-
1
def period_for_utc(utc)
-
real_timezone.period_for_utc(utc)
-
end
-
-
# Returns the set of TimezonePeriod instances that are valid for the given
-
# local time as an array. If you just want a single period, use
-
# period_for_local instead and specify how abiguities should be resolved.
-
# Returns an empty array if no periods are found for the given time.
-
1
def periods_for_local(local)
-
real_timezone.periods_for_local(local)
-
end
-
-
# Dumps this TimezoneProxy for marshalling.
-
1
def _dump(limit)
-
identifier
-
end
-
-
# Loads a marshalled TimezoneProxy.
-
1
def self._load(data)
-
TimezoneProxy.new(data)
-
end
-
-
1
private
-
1
def setup(identifier)
-
1
@identifier = identifier
-
1
@real_timezone = nil
-
end
-
-
1
def real_timezone
-
@real_timezone ||= Timezone.get(@identifier)
-
end
-
end
-
end
-
#--
-
# Copyright (c) 2006-2010 Philip Ross
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
-
# of this software and associated documentation files (the "Software"), to deal
-
# in the Software without restriction, including without limitation the rights
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-
# copies of the Software, and to permit persons to whom the Software is
-
# furnished to do so, subject to the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be included in all
-
# copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-
# THE SOFTWARE.
-
#++
-
-
1
require 'date'
-
-
1
module TZInfo
-
# Represents an offset defined in a Timezone data file.
-
1
class TimezoneTransitionInfo #:nodoc:
-
# The offset this transition changes to (a TimezoneOffsetInfo instance).
-
1
attr_reader :offset
-
-
# The offset this transition changes from (a TimezoneOffsetInfo instance).
-
1
attr_reader :previous_offset
-
-
# The numerator of the DateTime if the transition time is defined as a
-
# DateTime, otherwise the transition time as a timestamp.
-
1
attr_reader :numerator_or_time
-
1
protected :numerator_or_time
-
-
# Either the denominotor of the DateTime if the transition time is defined
-
# as a DateTime, otherwise nil.
-
1
attr_reader :denominator
-
1
protected :denominator
-
-
# Creates a new TimezoneTransitionInfo with the given offset,
-
# previous_offset (both TimezoneOffsetInfo instances) and UTC time.
-
# if denominator is nil, numerator_or_time is treated as a number of
-
# seconds since the epoch. If denominator is specified numerator_or_time
-
# and denominator are used to create a DateTime as follows:
-
#
-
# DateTime.new!(Rational.send(:new!, numerator_or_time, denominator), 0, Date::ITALY)
-
#
-
# For performance reasons, the numerator and denominator must be specified
-
# in their lowest form.
-
1
def initialize(offset, previous_offset, numerator_or_time, denominator = nil)
-
@offset = offset
-
@previous_offset = previous_offset
-
@numerator_or_time = numerator_or_time
-
@denominator = denominator
-
-
@at = nil
-
@local_end = nil
-
@local_start = nil
-
end
-
-
# A TimeOrDateTime instance representing the UTC time when this transition
-
# occurs.
-
1
def at
-
unless @at
-
unless @denominator
-
@at = TimeOrDateTime.new(@numerator_or_time)
-
else
-
r = RubyCoreSupport.rational_new!(@numerator_or_time, @denominator)
-
dt = RubyCoreSupport.datetime_new!(r, 0, Date::ITALY)
-
@at = TimeOrDateTime.new(dt)
-
end
-
end
-
-
@at
-
end
-
-
# A TimeOrDateTime instance representing the local time when this transition
-
# causes the previous observance to end (calculated from at using
-
# previous_offset).
-
1
def local_end
-
@local_end = at.add_with_convert(@previous_offset.utc_total_offset) unless @local_end
-
@local_end
-
end
-
-
# A TimeOrDateTime instance representing the local time when this transition
-
# causes the next observance to start (calculated from at using offset).
-
1
def local_start
-
@local_start = at.add_with_convert(@offset.utc_total_offset) unless @local_start
-
@local_start
-
end
-
-
# Returns true if this TimezoneTransitionInfo is equal to the given
-
# TimezoneTransitionInfo. Two TimezoneTransitionInfo instances are
-
# considered to be equal by == if offset, previous_offset and at are all
-
# equal.
-
1
def ==(tti)
-
tti.respond_to?(:offset) && tti.respond_to?(:previous_offset) && tti.respond_to?(:at) &&
-
offset == tti.offset && previous_offset == tti.previous_offset && at == tti.at
-
end
-
-
# Returns true if this TimezoneTransitionInfo is equal to the given
-
# TimezoneTransitionInfo. Two TimezoneTransitionInfo instances are
-
# considered to be equal by eql? if offset, previous_offset,
-
# numerator_or_time and denominator are all equal. This is stronger than ==,
-
# which just requires the at times to be equal regardless of how they were
-
# originally specified.
-
1
def eql?(tti)
-
tti.respond_to?(:offset) && tti.respond_to?(:previous_offset) &&
-
tti.respond_to?(:numerator_or_time) && tti.respond_to?(:denominator) &&
-
offset == tti.offset && previous_offset == tti.previous_offset &&
-
numerator_or_time == tti.numerator_or_time && denominator == tti.denominator
-
end
-
-
# Returns a hash of this TimezoneTransitionInfo instance.
-
1
def hash
-
@offset.hash ^ @previous_offset.hash ^ @numerator_or_time.hash ^ @denominator.hash
-
end
-
-
# Returns internal object state as a programmer-readable string.
-
1
def inspect
-
"#<#{self.class}: #{at.inspect},#{@offset.inspect}>"
-
end
-
end
-
end
-
# encoding: UTF-8
-
-
1
require "execjs"
-
1
require "multi_json"
-
-
1
class Uglifier
-
1
Error = ExecJS::Error
-
# MultiJson.engine = :json_gem
-
-
# Default options for compilation
-
1
DEFAULTS = {
-
:mangle => true, # Mangle variable and function names, use :vars to skip function mangling
-
:toplevel => false, # Mangle top-level variable names
-
:except => ["$super"], # Variable names to be excluded from mangling
-
:max_line_length => 32 * 1024, # Maximum line length
-
:squeeze => true, # Squeeze code resulting in smaller, but less-readable code
-
:seqs => true, # Reduce consecutive statements in blocks into single statement
-
:dead_code => true, # Remove dead code (e.g. after return)
-
:lift_vars => false, # Lift all var declarations at the start of the scope
-
:unsafe => false, # Optimizations known to be unsafe in some situations
-
:copyright => true, # Show copyright message
-
:ascii_only => false, # Encode non-ASCII characters as Unicode code points
-
:inline_script => false, # Escape </script
-
:quote_keys => false, # Quote keys in object literals
-
:beautify => false, # Ouput indented code
-
:beautify_options => {
-
:indent_level => 4,
-
:indent_start => 0,
-
:space_colon => false
-
}
-
}
-
-
1
SourcePath = File.expand_path("../uglify.js", __FILE__)
-
1
ES5FallbackPath = File.expand_path("../es5.js", __FILE__)
-
-
# Minifies JavaScript code using implicit context.
-
#
-
# source should be a String or IO object containing valid JavaScript.
-
# options contain optional overrides to Uglifier::DEFAULTS
-
#
-
# Returns minified code as String
-
1
def self.compile(source, options = {})
-
self.new(options).compile(source)
-
end
-
-
# Initialize new context for Uglifier with given options
-
#
-
# options - Hash of options to override Uglifier::DEFAULTS
-
1
def initialize(options = {})
-
@options = DEFAULTS.merge(options)
-
@context = ExecJS.compile(File.open(ES5FallbackPath, "r:UTF-8").read + File.open(SourcePath, "r:UTF-8").read)
-
end
-
-
# Minifies JavaScript code
-
#
-
# source should be a String or IO object containing valid JavaScript.
-
#
-
# Returns minified code as String
-
1
def compile(source)
-
source = source.respond_to?(:read) ? source.read : source.to_s
-
-
js = []
-
js << "var result = '';"
-
js << "var source = #{MultiJson.encode(source)};"
-
js << "var ast = UglifyJS.parser.parse(source);"
-
-
if @options[:lift_vars]
-
js << "ast = UglifyJS.uglify.ast_lift_variables(ast);"
-
end
-
-
if @options[:copyright]
-
js << <<-JS
-
var comments = UglifyJS.parser.tokenizer(source)().comments_before;
-
for (var i = 0; i < comments.length; i++) {
-
var c = comments[i];
-
result += (c.type == "comment1") ? "//"+c.value+"\\n" : "/*"+c.value+"*/\\n";
-
}
-
JS
-
end
-
-
if @options[:mangle]
-
js << "ast = UglifyJS.uglify.ast_mangle(ast, #{MultiJson.encode(mangle_options)});"
-
end
-
-
if @options[:squeeze]
-
js << "ast = UglifyJS.uglify.ast_squeeze(ast, #{MultiJson.encode(squeeze_options)});"
-
end
-
-
if @options[:unsafe]
-
js << "ast = UglifyJS.uglify.ast_squeeze_more(ast);"
-
end
-
-
js << "result += UglifyJS.uglify.gen_code(ast, #{MultiJson.encode(gen_code_options)});"
-
-
if !@options[:beautify] && @options[:max_line_length]
-
js << "result = UglifyJS.uglify.split_lines(result, #{@options[:max_line_length].to_i})"
-
end
-
-
js << "return result + ';';"
-
-
@context.exec js.join("\n")
-
end
-
1
alias_method :compress, :compile
-
-
1
private
-
-
1
def mangle_options
-
{
-
"toplevel" => @options[:toplevel],
-
"defines" => {},
-
"except" => @options[:except],
-
"no_functions" => @options[:mangle] == :vars
-
}
-
end
-
-
1
def squeeze_options
-
{
-
"make_seqs" => @options[:seqs],
-
"dead_code" => @options[:dead_code],
-
"keep_comps" => !@options[:unsafe]
-
}
-
end
-
-
1
def gen_code_options
-
options = {
-
:ascii_only => @options[:ascii_only],
-
:inline_script => @options[:inline_script],
-
:quote_keys => @options[:quote_keys]
-
}
-
-
if @options[:beautify]
-
options.merge(:beautify => true).merge(@options[:beautify_options])
-
else
-
options
-
end
-
end
-
end
-
# You will paginate!
-
1
module WillPaginate
-
end
-
-
1
if defined?(Rails::Railtie)
-
1
require 'will_paginate/railtie'
-
elsif defined?(Rails::Initializer)
-
raise "will_paginate 3.0 is not compatible with Rails 2.3 or older"
-
end
-
-
1
if defined?(Merb::AbstractController)
-
require 'will_paginate/view_helpers/merb'
-
-
Merb::BootLoader.before_app_loads do
-
adapters = { :datamapper => 'data_mapper', :activerecord => 'active_record', :sequel => 'sequel' }
-
# auto-load the right ORM adapter
-
if adapter = adapters[Merb.orm]
-
require "will_paginate/#{adapter}"
-
end
-
end
-
end
-
-
1
if defined?(Sinatra) and Sinatra.respond_to? :register
-
require 'will_paginate/view_helpers/sinatra'
-
end
-
1
require 'will_paginate/per_page'
-
1
require 'will_paginate/page_number'
-
1
require 'will_paginate/collection'
-
1
require 'active_record'
-
-
1
module WillPaginate
-
# = Paginating finders for ActiveRecord models
-
#
-
# WillPaginate adds +paginate+, +per_page+ and other methods to
-
# ActiveRecord::Base class methods and associations.
-
#
-
# In short, paginating finders are equivalent to ActiveRecord finders; the
-
# only difference is that we start with "paginate" instead of "find" and
-
# that <tt>:page</tt> is required parameter:
-
#
-
# @posts = Post.paginate :all, :page => params[:page], :order => 'created_at DESC'
-
#
-
1
module ActiveRecord
-
# makes a Relation look like WillPaginate::Collection
-
1
module RelationMethods
-
1
include WillPaginate::CollectionMethods
-
-
1
attr_accessor :current_page
-
1
attr_writer :total_entries, :wp_count_options
-
-
1
def per_page(value = nil)
-
if value.nil? then limit_value
-
else limit(value)
-
end
-
end
-
-
# TODO: solve with less relation clones and code dups
-
1
def limit(num)
-
rel = super
-
if rel.current_page
-
rel.offset rel.current_page.to_offset(rel.limit_value).to_i
-
else
-
rel
-
end
-
end
-
-
1
def offset(value = nil)
-
if value.nil? then offset_value
-
else super(value)
-
end
-
end
-
-
1
def total_entries
-
@total_entries ||= begin
-
if loaded? and size < limit_value and (current_page == 1 or size > 0)
-
offset_value + size
-
else
-
@total_entries_queried = true
-
result = count
-
result = result.size if result.respond_to?(:size) and !result.is_a?(Integer)
-
result
-
end
-
end
-
end
-
-
1
def count
-
if limit_value
-
excluded = [:order, :limit, :offset]
-
excluded << :includes unless eager_loading?
-
rel = self.except(*excluded)
-
# TODO: hack. decide whether to keep
-
rel = rel.apply_finder_options(@wp_count_options) if defined? @wp_count_options
-
rel.count
-
else
-
super
-
end
-
end
-
-
# workaround for Active Record 3.0
-
1
def size
-
if !loaded? and limit_value and group_values.empty?
-
[super, limit_value].min
-
else
-
super
-
end
-
end
-
-
# overloaded to be pagination-aware
-
1
def empty?
-
if !loaded? and offset_value
-
result = count
-
result = result.size if result.respond_to?(:size) and !result.is_a?(Integer)
-
result <= offset_value
-
else
-
super
-
end
-
end
-
-
1
def clone
-
copy_will_paginate_data super
-
end
-
-
# workaround for Active Record 3.0
-
1
def scoped(options = nil)
-
copy_will_paginate_data super
-
end
-
-
1
def to_a
-
if current_page.nil? then super # workaround for Active Record 3.0
-
else
-
::WillPaginate::Collection.create(current_page, limit_value) do |col|
-
col.replace super
-
col.total_entries ||= total_entries
-
end
-
end
-
end
-
-
1
private
-
-
1
def copy_will_paginate_data(other)
-
other.current_page = current_page unless other.current_page
-
other.total_entries = nil if defined? @total_entries_queried
-
other.wp_count_options = @wp_count_options if defined? @wp_count_options
-
other
-
end
-
end
-
-
1
module Pagination
-
1
def paginate(options)
-
options = options.dup
-
pagenum = options.fetch(:page) { raise ArgumentError, ":page parameter required" }
-
per_page = options.delete(:per_page) || self.per_page
-
total = options.delete(:total_entries)
-
-
count_options = options.delete(:count)
-
options.delete(:page)
-
-
rel = limit(per_page.to_i).page(pagenum)
-
rel = rel.apply_finder_options(options) if options.any?
-
rel.wp_count_options = count_options if count_options
-
rel.total_entries = total.to_i unless total.blank?
-
rel
-
end
-
-
1
def page(num)
-
rel = scoped.extending(RelationMethods)
-
pagenum = ::WillPaginate::PageNumber(num.nil? ? 1 : num)
-
per_page = rel.limit_value || self.per_page
-
rel = rel.offset(pagenum.to_offset(per_page).to_i)
-
rel = rel.limit(per_page) unless rel.limit_value
-
rel.current_page = pagenum
-
rel
-
end
-
end
-
-
1
module BaseMethods
-
# Wraps +find_by_sql+ by simply adding LIMIT and OFFSET to your SQL string
-
# based on the params otherwise used by paginating finds: +page+ and
-
# +per_page+.
-
#
-
# Example:
-
#
-
# @developers = Developer.paginate_by_sql ['select * from developers where salary > ?', 80000],
-
# :page => params[:page], :per_page => 3
-
#
-
# A query for counting rows will automatically be generated if you don't
-
# supply <tt>:total_entries</tt>. If you experience problems with this
-
# generated SQL, you might want to perform the count manually in your
-
# application.
-
#
-
1
def paginate_by_sql(sql, options)
-
pagenum = options.fetch(:page) { raise ArgumentError, ":page parameter required" } || 1
-
per_page = options[:per_page] || self.per_page
-
total = options[:total_entries]
-
-
WillPaginate::Collection.create(pagenum, per_page, total) do |pager|
-
query = sanitize_sql(sql.dup)
-
original_query = query.dup
-
oracle = self.connection.adapter_name =~ /^(oracle|oci$)/i
-
-
# add limit, offset
-
if oracle
-
query = <<-SQL
-
SELECT * FROM (
-
SELECT rownum rnum, a.* FROM (#{query}) a
-
WHERE rownum <= #{pager.offset + pager.per_page}
-
) WHERE rnum >= #{pager.offset}
-
SQL
-
else
-
query << " LIMIT #{pager.per_page} OFFSET #{pager.offset}"
-
end
-
-
# perfom the find
-
pager.replace find_by_sql(query)
-
-
unless pager.total_entries
-
count_query = original_query.sub /\bORDER\s+BY\s+[\w`,\s.]+$/mi, ''
-
count_query = "SELECT COUNT(*) FROM (#{count_query})"
-
count_query << ' AS count_table' unless oracle
-
# perform the count query
-
pager.total_entries = count_by_sql(count_query)
-
end
-
end
-
end
-
end
-
-
# mix everything into Active Record
-
1
::ActiveRecord::Base.extend PerPage
-
1
::ActiveRecord::Base.extend Pagination
-
1
::ActiveRecord::Base.extend BaseMethods
-
-
1
klasses = [::ActiveRecord::Relation]
-
1
if defined? ::ActiveRecord::Associations::CollectionProxy
-
1
klasses << ::ActiveRecord::Associations::CollectionProxy
-
else
-
klasses << ::ActiveRecord::Associations::AssociationCollection
-
end
-
-
# support pagination on associations and scopes
-
3
klasses.each { |klass| klass.send(:include, Pagination) }
-
end
-
end
-
1
require 'will_paginate/collection'
-
-
1
class Array
-
# Paginates a static array (extracting a subset of it). The result is a
-
# WillPaginate::Collection instance, which is an array with a few more
-
# properties about its paginated state.
-
#
-
# Parameters:
-
# * <tt>:page</tt> - current page, defaults to 1
-
# * <tt>:per_page</tt> - limit of items per page, defaults to 30
-
# * <tt>:total_entries</tt> - total number of items in the array, defaults to
-
# <tt>array.length</tt> (obviously)
-
#
-
# Example:
-
# arr = ['a', 'b', 'c', 'd', 'e']
-
# paged = arr.paginate(:per_page => 2) #-> ['a', 'b']
-
# paged.total_entries #-> 5
-
# arr.paginate(:page => 2, :per_page => 2) #-> ['c', 'd']
-
# arr.paginate(:page => 3, :per_page => 2) #-> ['e']
-
#
-
# This method was originally {suggested by Desi
-
# McAdam}[http://www.desimcadam.com/archives/8] and later proved to be the
-
# most useful method of will_paginate library.
-
1
def paginate(options = {})
-
page = options[:page] || 1
-
per_page = options[:per_page] || WillPaginate.per_page
-
total = options[:total_entries] || self.length
-
-
WillPaginate::Collection.create(page, per_page, total) do |pager|
-
pager.replace self[pager.offset, pager.per_page].to_a
-
end
-
end
-
end
-
1
require 'will_paginate/per_page'
-
1
require 'will_paginate/page_number'
-
-
1
module WillPaginate
-
# Any will_paginate-compatible collection should have these methods:
-
#
-
# current_page, per_page, offset, total_entries, total_pages
-
#
-
# It can also define some of these optional methods:
-
#
-
# out_of_bounds?, previous_page, next_page
-
#
-
# This module provides few of these methods.
-
1
module CollectionMethods
-
1
def total_pages
-
total_entries.zero? ? 1 : (total_entries / per_page.to_f).ceil
-
end
-
-
# current_page - 1 or nil if there is no previous page
-
1
def previous_page
-
current_page > 1 ? (current_page - 1) : nil
-
end
-
-
# current_page + 1 or nil if there is no next page
-
1
def next_page
-
current_page < total_pages ? (current_page + 1) : nil
-
end
-
-
# Helper method that is true when someone tries to fetch a page with a
-
# larger number than the last page. Can be used in combination with flashes
-
# and redirecting.
-
1
def out_of_bounds?
-
current_page > total_pages
-
end
-
end
-
-
# = The key to pagination
-
# Arrays returned from paginating finds are, in fact, instances of this little
-
# class. You may think of WillPaginate::Collection as an ordinary array with
-
# some extra properties. Those properties are used by view helpers to generate
-
# correct page links.
-
#
-
# WillPaginate::Collection also assists in rolling out your own pagination
-
# solutions: see +create+.
-
#
-
# If you are writing a library that provides a collection which you would like
-
# to conform to this API, you don't have to copy these methods over; simply
-
# make your plugin/gem dependant on this library and do:
-
#
-
# require 'will_paginate/collection'
-
# # WillPaginate::Collection is now available for use
-
1
class Collection < Array
-
1
include CollectionMethods
-
-
1
attr_reader :current_page, :per_page, :total_entries
-
-
# Arguments to the constructor are the current page number, per-page limit
-
# and the total number of entries. The last argument is optional because it
-
# is best to do lazy counting; in other words, count *conditionally* after
-
# populating the collection using the +replace+ method.
-
1
def initialize(page, per_page = WillPaginate.per_page, total = nil)
-
@current_page = WillPaginate::PageNumber(page)
-
@per_page = per_page.to_i
-
self.total_entries = total if total
-
end
-
-
# Just like +new+, but yields the object after instantiation and returns it
-
# afterwards. This is very useful for manual pagination:
-
#
-
# @entries = WillPaginate::Collection.create(1, 10) do |pager|
-
# result = Post.find(:all, :limit => pager.per_page, :offset => pager.offset)
-
# # inject the result array into the paginated collection:
-
# pager.replace(result)
-
#
-
# unless pager.total_entries
-
# # the pager didn't manage to guess the total count, do it manually
-
# pager.total_entries = Post.count
-
# end
-
# end
-
#
-
# The possibilities with this are endless. For another example, here is how
-
# WillPaginate used to define pagination for Array instances:
-
#
-
# Array.class_eval do
-
# def paginate(page = 1, per_page = 15)
-
# WillPaginate::Collection.create(page, per_page, size) do |pager|
-
# pager.replace self[pager.offset, pager.per_page].to_a
-
# end
-
# end
-
# end
-
#
-
# The Array#paginate API has since then changed, but this still serves as a
-
# fine example of WillPaginate::Collection usage.
-
1
def self.create(page, per_page, total = nil)
-
pager = new(page, per_page, total)
-
yield pager
-
pager
-
end
-
-
# Current offset of the paginated collection. If we're on the first page,
-
# it is always 0. If we're on the 2nd page and there are 30 entries per page,
-
# the offset is 30. This property is useful if you want to render ordinals
-
# side by side with records in the view: simply start with offset + 1.
-
1
def offset
-
current_page.to_offset(per_page).to_i
-
end
-
-
1
def total_entries=(number)
-
@total_entries = number.to_i
-
end
-
-
# This is a magic wrapper for the original Array#replace method. It serves
-
# for populating the paginated collection after initialization.
-
#
-
# Why magic? Because it tries to guess the total number of entries judging
-
# by the size of given array. If it is shorter than +per_page+ limit, then we
-
# know we're on the last page. This trick is very useful for avoiding
-
# unnecessary hits to the database to do the counting after we fetched the
-
# data for the current page.
-
#
-
# However, after using +replace+ you should always test the value of
-
# +total_entries+ and set it to a proper value if it's +nil+. See the example
-
# in +create+.
-
1
def replace(array)
-
result = super
-
-
# The collection is shorter then page limit? Rejoice, because
-
# then we know that we are on the last page!
-
if total_entries.nil? and length < per_page and (current_page == 1 or length > 0)
-
self.total_entries = offset + length
-
end
-
-
result
-
end
-
end
-
end
-
1
require 'set'
-
-
# copied from ActiveSupport so we don't depend on it
-
-
1
unless Hash.method_defined? :except
-
Hash.class_eval do
-
# Returns a new hash without the given keys.
-
def except(*keys)
-
rejected = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
-
reject { |key,| rejected.include?(key) }
-
end
-
-
# Replaces the hash without only the given keys.
-
def except!(*keys)
-
replace(except(*keys))
-
end
-
end
-
end
-
-
1
unless String.method_defined? :underscore
-
String.class_eval do
-
def underscore
-
self.to_s.gsub(/::/, '/').
-
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
-
gsub(/([a-z\d])([A-Z])/,'\1_\2').
-
tr("-", "_").
-
downcase
-
end
-
end
-
end
-
1
module WillPaginate::Deprecation
-
1
class << self
-
1
def warn(message, stack = caller)
-
offending_line = origin_of_call(stack)
-
full_message = "DEPRECATION WARNING: #{message} (called from #{offending_line})"
-
logger = rails_logger || Kernel
-
logger.warn full_message
-
end
-
-
1
private
-
-
1
def rails_logger
-
defined?(Rails) && Rails.logger
-
end
-
-
1
def origin_of_call(stack)
-
lib_root = File.expand_path('../../..', __FILE__)
-
stack.find { |line| line.index(lib_root) != 0 } || stack.first
-
end
-
end
-
-
1
class Hash < ::Hash
-
1
def initialize(values = {})
-
1
super()
-
1
update values
-
1
@deprecated = {}
-
end
-
-
1
def []=(key, value)
-
check_deprecated(key, value)
-
super
-
end
-
-
1
def deprecate_key(*keys)
-
2
message = block_given? ? Proc.new : keys.pop
-
5
Array(keys).each { |key| @deprecated[key] = message }
-
end
-
-
1
def merge(another)
-
to_hash.update(another)
-
end
-
-
1
def to_hash
-
::Hash.new.update(self)
-
end
-
-
1
private
-
-
1
def check_deprecated(key, value)
-
if msg = @deprecated[key] and (!msg.respond_to?(:call) or (msg = msg.call(key, value)))
-
WillPaginate::Deprecation.warn(msg)
-
end
-
end
-
end
-
end
-
1
module WillPaginate
-
1
module I18n
-
1
def self.locale_dir
-
1
File.expand_path('../locale', __FILE__)
-
end
-
-
1
def self.load_path
-
1
Dir["#{locale_dir}/*.{rb,yml}"]
-
end
-
-
1
def will_paginate_translate(keys, options = {})
-
if defined? ::I18n
-
defaults = Array(keys).dup
-
defaults << Proc.new if block_given?
-
::I18n.translate(defaults.shift, options.merge(:default => defaults, :scope => :will_paginate))
-
else
-
key = Array === keys ? keys.first : keys
-
yield key, options
-
end
-
end
-
end
-
end
-
1
require 'delegate'
-
1
require 'forwardable'
-
-
1
module WillPaginate
-
# a module that page number exceptions are tagged with
-
1
module InvalidPage; end
-
-
# integer representing a page number
-
1
class PageNumber < DelegateClass(Integer)
-
# a value larger than this is not supported in SQL queries
-
1
BIGINT = 9223372036854775807
-
-
1
extend Forwardable
-
-
1
def initialize(value, name)
-
value = Integer(value)
-
if 'offset' == name ? (value < 0 or value > BIGINT) : value < 1
-
raise RangeError, "invalid #{name}: #{value.inspect}"
-
end
-
@name = name
-
super(value)
-
rescue ArgumentError, TypeError, RangeError => error
-
error.extend InvalidPage
-
raise error
-
end
-
-
1
alias_method :to_i, :__getobj__
-
-
1
def inspect
-
"#{@name} #{to_i}"
-
end
-
-
1
def to_offset(per_page)
-
PageNumber.new((to_i - 1) * per_page.to_i, 'offset')
-
end
-
-
1
def kind_of?(klass)
-
super || to_i.kind_of?(klass)
-
end
-
1
alias is_a? kind_of?
-
end
-
-
# Ultrahax: makes `Fixnum === current_page` checks pass
-
1
Numeric.extend Module.new {
-
1
def ===(obj)
-
3
obj.instance_of? PageNumber or super
-
end
-
}
-
-
# An idemptotent coercion method
-
1
def self.PageNumber(value, name = 'page')
-
case value
-
when PageNumber then value
-
else PageNumber.new(value, name)
-
end
-
end
-
end
-
1
module WillPaginate
-
1
module PerPage
-
1
def per_page
-
12
defined?(@per_page) ? @per_page : WillPaginate.per_page
-
end
-
-
1
def per_page=(limit)
-
10
@per_page = limit.to_i
-
end
-
-
1
def self.extended(base)
-
2
base.extend Inheritance if base.is_a? Class
-
end
-
-
1
module Inheritance
-
1
def inherited(subclass)
-
9
super
-
9
subclass.per_page = self.per_page
-
end
-
end
-
end
-
-
1
extend PerPage
-
-
# default number of items per page
-
1
self.per_page = 30
-
end
-
1
require 'will_paginate'
-
1
require 'will_paginate/page_number'
-
1
require 'will_paginate/collection'
-
1
require 'will_paginate/i18n'
-
-
1
module WillPaginate
-
1
class Railtie < Rails::Railtie
-
1
initializer "will_paginate" do |app|
-
1
ActiveSupport.on_load :active_record do
-
1
require 'will_paginate/active_record'
-
end
-
-
1
ActiveSupport.on_load :action_controller do
-
1
WillPaginate::Railtie.setup_actioncontroller
-
end
-
-
1
ActiveSupport.on_load :action_view do
-
1
require 'will_paginate/view_helpers/action_view'
-
end
-
-
1
self.class.add_locale_path config
-
-
# early access to ViewHelpers.pagination_options
-
1
require 'will_paginate/view_helpers'
-
end
-
-
1
def self.setup_actioncontroller
-
1
( defined?(ActionDispatch::ExceptionWrapper) ?
-
ActionDispatch::ExceptionWrapper : ActionDispatch::ShowExceptions
-
).send :include, ShowExceptionsPatch
-
1
ActionController::Base.extend ControllerRescuePatch
-
end
-
-
1
def self.add_locale_path(config)
-
1
config.i18n.railties_load_path.unshift(*WillPaginate::I18n.load_path)
-
end
-
-
# Extending the exception handler middleware so it properly detects
-
# WillPaginate::InvalidPage regardless of it being a tag module.
-
1
module ShowExceptionsPatch
-
1
extend ActiveSupport::Concern
-
2
included { alias_method_chain :status_code, :paginate }
-
1
private
-
1
def status_code_with_paginate(exception = @exception)
-
if exception.is_a?(WillPaginate::InvalidPage) or
-
(exception.respond_to?(:original_exception) &&
-
exception.original_exception.is_a?(WillPaginate::InvalidPage))
-
Rack::Utils.status_code(:not_found)
-
else
-
original_method = method(:status_code_without_paginate)
-
if original_method.arity != 0
-
original_method.call(exception)
-
else
-
original_method.call()
-
end
-
end
-
end
-
end
-
-
1
module ControllerRescuePatch
-
1
def rescue_from(*args, &block)
-
if idx = args.index(WillPaginate::InvalidPage)
-
args[idx] = args[idx].name
-
end
-
super(*args, &block)
-
end
-
end
-
end
-
end
-
# encoding: utf-8
-
1
require 'will_paginate/core_ext'
-
1
require 'will_paginate/i18n'
-
1
require 'will_paginate/deprecation'
-
-
1
module WillPaginate
-
# = Will Paginate view helpers
-
#
-
# The main view helper is +will_paginate+. It renders the pagination links
-
# for the given collection. The helper itself is lightweight and serves only
-
# as a wrapper around LinkRenderer instantiation; the renderer then does
-
# all the hard work of generating the HTML.
-
1
module ViewHelpers
-
1
class << self
-
# Write to this hash to override default options on the global level:
-
#
-
# WillPaginate::ViewHelpers.pagination_options[:page_links] = false
-
#
-
1
attr_accessor :pagination_options
-
end
-
-
# default view options
-
1
self.pagination_options = Deprecation::Hash.new \
-
:class => 'pagination',
-
:previous_label => nil,
-
:next_label => nil,
-
:inner_window => 4, # links around the current page
-
:outer_window => 1, # links around beginning and end
-
:link_separator => ' ', # single space is friendly to spiders and non-graphic browsers
-
:param_name => :page,
-
:params => nil,
-
:page_links => true,
-
:container => true
-
-
1
label_deprecation = Proc.new { |key, value|
-
"set the 'will_paginate.#{key}' key in your i18n locale instead of editing pagination_options" if defined? Rails
-
}
-
1
pagination_options.deprecate_key(:previous_label, :next_label, &label_deprecation)
-
1
pagination_options.deprecate_key(:renderer) { |key, _| "pagination_options[#{key.inspect}] shouldn't be set globally" }
-
-
1
include WillPaginate::I18n
-
-
# Returns HTML representing page links for a WillPaginate::Collection-like object.
-
# In case there is no more than one page in total, nil is returned.
-
#
-
# ==== Options
-
# * <tt>:class</tt> -- CSS class name for the generated DIV (default: "pagination")
-
# * <tt>:previous_label</tt> -- default: "« Previous"
-
# * <tt>:next_label</tt> -- default: "Next »"
-
# * <tt>:page_links</tt> -- when false, only previous/next links are rendered (default: true)
-
# * <tt>:inner_window</tt> -- how many links are shown around the current page (default: 4)
-
# * <tt>:outer_window</tt> -- how many links are around the first and the last page (default: 1)
-
# * <tt>:link_separator</tt> -- string separator for page HTML elements (default: single space)
-
# * <tt>:param_name</tt> -- parameter name for page number in URLs (default: <tt>:page</tt>)
-
# * <tt>:params</tt> -- additional parameters when generating pagination links
-
# (eg. <tt>:controller => "foo", :action => nil</tt>)
-
# * <tt>:renderer</tt> -- class name, class or instance of a link renderer (default in Rails:
-
# <tt>WillPaginate::ActionView::LinkRenderer</tt>)
-
# * <tt>:page_links</tt> -- when false, only previous/next links are rendered (default: true)
-
# * <tt>:container</tt> -- toggles rendering of the DIV container for pagination links, set to
-
# false only when you are rendering your own pagination markup (default: true)
-
#
-
# All options not recognized by will_paginate will become HTML attributes on the container
-
# element for pagination links (the DIV). For example:
-
#
-
# <%= will_paginate @posts, :style => 'color:blue' %>
-
#
-
# will result in:
-
#
-
# <div class="pagination" style="color:blue"> ... </div>
-
#
-
1
def will_paginate(collection, options = {})
-
# early exit if there is nothing to render
-
return nil unless collection.total_pages > 1
-
-
options = WillPaginate::ViewHelpers.pagination_options.merge(options)
-
-
options[:previous_label] ||= will_paginate_translate(:previous_label) { '← Previous' }
-
options[:next_label] ||= will_paginate_translate(:next_label) { 'Next →' }
-
-
# get the renderer instance
-
renderer = case options[:renderer]
-
when nil
-
raise ArgumentError, ":renderer not specified"
-
when String
-
klass = if options[:renderer].respond_to? :constantize then options[:renderer].constantize
-
else Object.const_get(options[:renderer]) # poor man's constantize
-
end
-
klass.new
-
when Class then options[:renderer].new
-
else options[:renderer]
-
end
-
# render HTML for pagination
-
renderer.prepare collection, options, self
-
renderer.to_html
-
end
-
-
# Renders a message containing number of displayed vs. total entries.
-
#
-
# <%= page_entries_info @posts %>
-
# #-> Displaying posts 6 - 12 of 26 in total
-
#
-
# The default output contains HTML. Use ":html => false" for plain text.
-
1
def page_entries_info(collection, options = {})
-
model = options[:model]
-
model = collection.first.class unless model or collection.empty?
-
model ||= 'entry'
-
model_key = if model.respond_to? :model_name
-
model.model_name.i18n_key # ActiveModel::Naming
-
else
-
model.to_s.underscore
-
end
-
-
if options.fetch(:html, true)
-
b, eb = '<b>', '</b>'
-
sp = ' '
-
html_key = '_html'
-
else
-
b = eb = html_key = ''
-
sp = ' '
-
end
-
-
model_count = collection.total_pages > 1 ? 5 : collection.size
-
defaults = ["models.#{model_key}"]
-
defaults << Proc.new { |_, opts|
-
if model.respond_to? :model_name
-
model.model_name.human(:count => opts[:count])
-
else
-
name = model_key.to_s.tr('_', ' ')
-
raise "can't pluralize model name: #{model.inspect}" unless name.respond_to? :pluralize
-
opts[:count] == 1 ? name : name.pluralize
-
end
-
}
-
model_name = will_paginate_translate defaults, :count => model_count
-
-
if collection.total_pages < 2
-
i18n_key = :"page_entries_info.single_page#{html_key}"
-
keys = [:"#{model_key}.#{i18n_key}", i18n_key]
-
-
will_paginate_translate keys, :count => collection.size, :model => model_name do |_, opts|
-
case opts[:count]
-
when 0; "No #{opts[:model]} found"
-
when 1; "Displaying #{b}1#{eb} #{opts[:model]}"
-
else "Displaying #{b}all#{sp}#{opts[:count]}#{eb} #{opts[:model]}"
-
end
-
end
-
else
-
i18n_key = :"page_entries_info.multi_page#{html_key}"
-
keys = [:"#{model_key}.#{i18n_key}", i18n_key]
-
params = {
-
:model => model_name, :count => collection.total_entries,
-
:from => collection.offset + 1, :to => collection.offset + collection.length
-
}
-
will_paginate_translate keys, params do |_, opts|
-
%{Displaying %s #{b}%d#{sp}-#{sp}%d#{eb} of #{b}%d#{eb} in total} %
-
[ opts[:model], opts[:from], opts[:to], opts[:count] ]
-
end
-
end
-
end
-
end
-
end
-
1
require 'will_paginate/view_helpers'
-
1
require 'will_paginate/view_helpers/link_renderer'
-
-
1
module WillPaginate
-
# = ActionView helpers
-
#
-
# This module serves for availability in ActionView templates. It also adds a new
-
# view helper: +paginated_section+.
-
#
-
# == Using the helper without arguments
-
# If the helper is called without passing in the collection object, it will
-
# try to read from the instance variable inferred by the controller name.
-
# For example, calling +will_paginate+ while the current controller is
-
# PostsController will result in trying to read from the <tt>@posts</tt>
-
# variable. Example:
-
#
-
# <%= will_paginate :id => true %>
-
#
-
# ... will result in <tt>@post</tt> collection getting paginated:
-
#
-
# <div class="pagination" id="posts_pagination"> ... </div>
-
#
-
1
module ActionView
-
1
include ViewHelpers
-
-
1
def will_paginate(collection = nil, options = {}) #:nodoc:
-
options, collection = collection, nil if collection.is_a? Hash
-
collection ||= infer_collection_from_controller
-
-
options = options.symbolize_keys
-
options[:renderer] ||= LinkRenderer
-
-
super(collection, options).try(:html_safe)
-
end
-
-
1
def page_entries_info(collection = nil, options = {}) #:nodoc:
-
options, collection = collection, nil if collection.is_a? Hash
-
collection ||= infer_collection_from_controller
-
-
super(collection, options.symbolize_keys)
-
end
-
-
# Wrapper for rendering pagination links at both top and bottom of a block
-
# of content.
-
#
-
# <% paginated_section @posts do %>
-
# <ol id="posts">
-
# <% for post in @posts %>
-
# <li> ... </li>
-
# <% end %>
-
# </ol>
-
# <% end %>
-
#
-
# will result in:
-
#
-
# <div class="pagination"> ... </div>
-
# <ol id="posts">
-
# ...
-
# </ol>
-
# <div class="pagination"> ... </div>
-
#
-
# Arguments are passed to a <tt>will_paginate</tt> call, so the same options
-
# apply. Don't use the <tt>:id</tt> option; otherwise you'll finish with two
-
# blocks of pagination links sharing the same ID (which is invalid HTML).
-
1
def paginated_section(*args, &block)
-
pagination = will_paginate(*args)
-
if pagination
-
pagination + capture(&block) + pagination
-
else
-
capture(&block)
-
end
-
end
-
-
1
def will_paginate_translate(keys, options = {})
-
if respond_to? :translate
-
if Array === keys
-
defaults = keys.dup
-
key = defaults.shift
-
else
-
defaults = nil
-
key = keys
-
end
-
translate(key, options.merge(:default => defaults, :scope => :will_paginate))
-
else
-
super
-
end
-
end
-
-
1
protected
-
-
1
def infer_collection_from_controller
-
collection_name = "@#{controller.controller_name}"
-
collection = instance_variable_get(collection_name)
-
raise ArgumentError, "The #{collection_name} variable appears to be empty. Did you " +
-
"forget to pass the collection object for will_paginate?" if collection.nil?
-
collection
-
end
-
-
1
class LinkRenderer < ViewHelpers::LinkRenderer
-
1
protected
-
-
1
def default_url_params
-
{}
-
end
-
-
1
def url(page)
-
@base_url_params ||= begin
-
url_params = merge_get_params(default_url_params)
-
merge_optional_params(url_params)
-
end
-
-
url_params = @base_url_params.dup
-
add_current_page_param(url_params, page)
-
-
@template.url_for(url_params)
-
end
-
-
1
def merge_get_params(url_params)
-
if @template.respond_to? :request and @template.request and @template.request.get?
-
symbolized_update(url_params, @template.params)
-
end
-
url_params
-
end
-
-
1
def merge_optional_params(url_params)
-
symbolized_update(url_params, @options[:params]) if @options[:params]
-
url_params
-
end
-
-
1
def add_current_page_param(url_params, page)
-
unless param_name.index(/[^\w-]/)
-
url_params[param_name.to_sym] = page
-
else
-
page_param = parse_query_parameters("#{param_name}=#{page}")
-
symbolized_update(url_params, page_param)
-
end
-
end
-
-
1
private
-
-
1
def parse_query_parameters(params)
-
Rack::Utils.parse_nested_query(params)
-
end
-
end
-
-
1
::ActionView::Base.send :include, self
-
end
-
end
-
1
require 'cgi'
-
1
require 'will_paginate/core_ext'
-
1
require 'will_paginate/view_helpers'
-
1
require 'will_paginate/view_helpers/link_renderer_base'
-
-
1
module WillPaginate
-
1
module ViewHelpers
-
# This class does the heavy lifting of actually building the pagination
-
# links. It is used by +will_paginate+ helper internally.
-
1
class LinkRenderer < LinkRendererBase
-
-
# * +collection+ is a WillPaginate::Collection instance or any other object
-
# that conforms to that API
-
# * +options+ are forwarded from +will_paginate+ view helper
-
# * +template+ is the reference to the template being rendered
-
1
def prepare(collection, options, template)
-
super(collection, options)
-
@template = template
-
@container_attributes = @base_url_params = nil
-
end
-
-
# Process it! This method returns the complete HTML string which contains
-
# pagination links. Feel free to subclass LinkRenderer and change this
-
# method as you see fit.
-
1
def to_html
-
html = pagination.map do |item|
-
item.is_a?(Fixnum) ?
-
page_number(item) :
-
send(item)
-
end.join(@options[:link_separator])
-
-
@options[:container] ? html_container(html) : html
-
end
-
-
# Returns the subset of +options+ this instance was initialized with that
-
# represent HTML attributes for the container element of pagination links.
-
1
def container_attributes
-
@container_attributes ||= @options.except(*(ViewHelpers.pagination_options.keys + [:renderer] - [:class]))
-
end
-
-
1
protected
-
-
1
def page_number(page)
-
unless page == current_page
-
link(page, page, :rel => rel_value(page))
-
else
-
tag(:em, page, :class => 'current')
-
end
-
end
-
-
1
def gap
-
text = @template.will_paginate_translate(:page_gap) { '…' }
-
%(<span class="gap">#{text}</span>)
-
end
-
-
1
def previous_page
-
num = @collection.current_page > 1 && @collection.current_page - 1
-
previous_or_next_page(num, @options[:previous_label], 'previous_page')
-
end
-
-
1
def next_page
-
num = @collection.current_page < @collection.total_pages && @collection.current_page + 1
-
previous_or_next_page(num, @options[:next_label], 'next_page')
-
end
-
-
1
def previous_or_next_page(page, text, classname)
-
if page
-
link(text, page, :class => classname)
-
else
-
tag(:span, text, :class => classname + ' disabled')
-
end
-
end
-
-
1
def html_container(html)
-
tag(:div, html, container_attributes)
-
end
-
-
# Returns URL params for +page_link_or_span+, taking the current GET params
-
# and <tt>:params</tt> option into account.
-
1
def url(page)
-
raise NotImplementedError
-
end
-
-
1
private
-
-
1
def param_name
-
@options[:param_name].to_s
-
end
-
-
1
def link(text, target, attributes = {})
-
if target.is_a? Fixnum
-
attributes[:rel] = rel_value(target)
-
target = url(target)
-
end
-
attributes[:href] = target
-
tag(:a, text, attributes)
-
end
-
-
1
def tag(name, value, attributes = {})
-
string_attributes = attributes.inject('') do |attrs, pair|
-
unless pair.last.nil?
-
attrs << %( #{pair.first}="#{CGI::escapeHTML(pair.last.to_s)}")
-
end
-
attrs
-
end
-
"<#{name}#{string_attributes}>#{value}</#{name}>"
-
end
-
-
1
def rel_value(page)
-
case page
-
when @collection.current_page - 1; 'prev' + (page == 1 ? ' start' : '')
-
when @collection.current_page + 1; 'next'
-
when 1; 'start'
-
end
-
end
-
-
1
def symbolized_update(target, other)
-
other.each do |key, value|
-
key = key.to_sym
-
existing = target[key]
-
-
if value.is_a?(Hash) and (existing.is_a?(Hash) or existing.nil?)
-
symbolized_update(existing || (target[key] = {}), value)
-
else
-
target[key] = value
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module WillPaginate
-
1
module ViewHelpers
-
# This class does the heavy lifting of actually building the pagination
-
# links. It is used by +will_paginate+ helper internally.
-
1
class LinkRendererBase
-
-
# * +collection+ is a WillPaginate::Collection instance or any other object
-
# that conforms to that API
-
# * +options+ are forwarded from +will_paginate+ view helper
-
1
def prepare(collection, options)
-
@collection = collection
-
@options = options
-
-
# reset values in case we're re-using this instance
-
@total_pages = nil
-
end
-
-
1
def pagination
-
items = @options[:page_links] ? windowed_page_numbers : []
-
items.unshift :previous_page
-
items.push :next_page
-
end
-
-
1
protected
-
-
# Calculates visible page numbers using the <tt>:inner_window</tt> and
-
# <tt>:outer_window</tt> options.
-
1
def windowed_page_numbers
-
inner_window, outer_window = @options[:inner_window].to_i, @options[:outer_window].to_i
-
window_from = current_page - inner_window
-
window_to = current_page + inner_window
-
-
# adjust lower or upper limit if other is out of bounds
-
if window_to > total_pages
-
window_from -= window_to - total_pages
-
window_to = total_pages
-
end
-
if window_from < 1
-
window_to += 1 - window_from
-
window_from = 1
-
window_to = total_pages if window_to > total_pages
-
end
-
-
# these are always visible
-
middle = window_from..window_to
-
-
# left window
-
if outer_window + 3 < middle.first # there's a gap
-
left = (1..(outer_window + 1)).to_a
-
left << :gap
-
else # runs into visible pages
-
left = 1...middle.first
-
end
-
-
# right window
-
if total_pages - outer_window - 2 > middle.last # again, gap
-
right = ((total_pages - outer_window)..total_pages).to_a
-
right.unshift :gap
-
else # runs into visible pages
-
right = (middle.last + 1)..total_pages
-
end
-
-
left.to_a + middle.to_a + right.to_a
-
end
-
-
1
private
-
-
1
def current_page
-
@collection.current_page
-
end
-
-
1
def total_pages
-
@total_pages ||= @collection.total_pages
-
end
-
end
-
end
-
end
-
1
require 'nokogiri'
-
-
1
module XPath
-
1
autoload :Expression, 'xpath/expression'
-
1
autoload :Union, 'xpath/union'
-
1
autoload :HTML, 'xpath/html'
-
-
1
extend self
-
-
1
def self.generate
-
yield(Expression::Self.new)
-
end
-
-
1
def current
-
Expression::Self.new
-
end
-
-
1
def name
-
Expression::Name.new(current)
-
end
-
-
1
def descendant(*expressions)
-
Expression::Descendant.new(current, expressions)
-
end
-
-
1
def child(*expressions)
-
Expression::Child.new(current, expressions)
-
end
-
-
1
def anywhere(expression)
-
Expression::Anywhere.new(expression)
-
end
-
-
1
def attr(expression)
-
Expression::Attribute.new(current, expression)
-
end
-
-
1
def contains(expression)
-
Expression::Contains.new(current, expression)
-
end
-
-
1
def text
-
Expression::Text.new(current)
-
end
-
-
1
def var(name)
-
Expression::Variable.new(name)
-
end
-
-
1
def string
-
Expression::StringFunction.new(current)
-
end
-
-
1
def tag(name)
-
Expression::Tag.new(name)
-
end
-
-
1
def css(selector)
-
paths = Nokogiri::CSS.xpath_for(selector).map do |selector|
-
Expression::CSS.new(current, Expression::Literal.new(selector))
-
end
-
Union.new(*paths)
-
end
-
-
1
def varstring(name)
-
var(name).string_literal
-
end
-
end
-
# Read about factories at https://github.com/thoughtbot/factory_girl
-
-
1
FactoryGirl.define do
-
1
factory :educ, class: 'EducationCompany' do
-
1
type "EducationCompany"
-
1
name "MyEducationCompany"
-
1
representative = Person.new({ :first_name => "MyFirst", :last_name => "MyLast", :occupation => "MyJob", :type => "Individual" })
-
1
representative_role "Education"
-
end
-
1
factory :prtf, class: 'PortfolioCompany' do
-
1
type "PortfolioCompany"
-
1
name "MyPortfolioCompany"
-
1
representative = Person.new({ :first_name => "MyFirst", :last_name => "MyLast", :occupation => "MyJob", :type => "Individual" })
-
1
representative_role "Portfolio"
-
end
-
1
factory :prof, class: 'ProfessionalServiceProvider' do
-
1
type "ProfessionalServiceProvider"
-
1
name "MyProfessionalCompany"
-
1
representative = Person.new({ :first_name => "MyFirst", :last_name => "MyLast", :occupation => "MyJob", :type => "Individual" })
-
1
representative_role "Professional"
-
end
-
end
-
# Read about factories at https://github.com/thoughtbot/factory_girl
-
-
1
FactoryGirl.define do
-
1
factory :individual, class: 'Individual' do
-
1
type "Individual"
-
1
first_name "MyString"
-
1
last_name "MyString"
-
1
occupation "MyString"
-
end
-
1
factory :advisor, class: 'Advisor' do
-
1
type "Advisor"
-
1
first_name "MyString"
-
1
last_name "MyString"
-
1
occupation "MyString"
-
end
-
1
factory :boardmember, class: 'BoardMember' do
-
1
type "BoardMember"
-
1
first_name "MyString"
-
1
last_name "MyString"
-
1
occupation "MyString"
-
end
-
end
-
# Read about factories at https://github.com/thoughtbot/factory_girl
-
-
1
FactoryGirl.define do
-
1
factory :phone_number do
-
1
callable nil
-
1
type ""
-
1
number 1
-
end
-
end